1
0
mirror of synced 2025-01-31 04:13:50 +01:00

Add leaderboard API endpoint

This commit is contained in:
shiibe 2024-06-02 19:29:33 -04:00
parent ce276c790a
commit 35f2448e00
8 changed files with 188 additions and 2 deletions

View File

@ -11,7 +11,12 @@ namespace GameDatabase.Entities
public uint BestRate { get; set; } public uint BestRate { get; set; }
public CrownType BestCrown { get; set; } public CrownType BestCrown { get; set; }
public ScoreRank BestScoreRank { get; set; } public ScoreRank BestScoreRank { get; set; }
public uint GoodCount { get; set; }
public uint OkCount { get; set; }
public uint MissCount { get; set; }
public uint ComboCount { get; set; }
public uint HitCount { get; set; }
public uint DrumrollCount { get; set; }
public virtual UserDatum? Ba { get; set; } public virtual UserDatum? Ba { get; set; }
} }
} }

View File

@ -0,0 +1,6 @@
namespace SharedProject.Models.Responses;
public class SongLeaderboardResponse
{
public List<SongLeaderboard> Leaderboard { get; set; } = new();
}

View File

@ -46,6 +46,5 @@ public class SongBestData
public List<AiSectionBestData> AiSectionBestData { get; set; } = new(); public List<AiSectionBestData> AiSectionBestData { get; set; } = new();
public bool ShowAiData { get; set; } public bool ShowAiData { get; set; }
public List<SongPlayDatumDto> RecentPlayData { get; set; } = new(); public List<SongPlayDatumDto> RecentPlayData { get; set; } = new();
} }

View File

@ -0,0 +1,27 @@
using SharedProject.Enums;
namespace SharedProject.Models;
public class SongLeaderboard
{
public uint Rank { get; set; }
public uint Baid { get; set; }
public uint BestScore { get; set; }
public uint BestRate { get; set; }
public CrownType BestCrown { get; set; }
public ScoreRank BestScoreRank { get; set; }
public uint GoodCount { get; set; }
public uint OkCount { get; set; }
public uint MissCount { get; set; }
public uint ComboCount { get; set; }
public uint HitCount { get; set; }
public uint DrumrollCount { get; set; }
public string? UserName { get; set; }
}

View File

@ -0,0 +1,43 @@
using Microsoft.Extensions.Options;
using SharedProject.Models;
using SharedProject.Models.Responses;
using TaikoLocalServer.Filters;
using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Controllers.Api;
[ApiController]
[Route("api/[controller]")]
public class SongLeaderboardData(ISongLeaderboardService songLeaderboardService,IAuthService authService, IOptions<AuthSettings> settings)
: BaseController<SongLeaderboardData>
{
private readonly AuthSettings authSettings = settings.Value;
[HttpGet("{baid}/{songId}/{difficulty}")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public async Task<ActionResult<SongLeaderboardResponse>> GetSongLeaderboard(uint baid, uint songId, uint difficulty)
{
if (authSettings.AuthenticationRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
if (tokenInfo.Value.baid != baid && !tokenInfo.Value.isAdmin)
{
return Forbid();
}
}
var leaderboard = await songLeaderboardService.GetSongLeaderboard(songId, (Difficulty)difficulty, (int)baid);
return Ok(new SongLeaderboardResponse
{
Leaderboard = leaderboard
});
}
}

View File

@ -0,0 +1,7 @@
namespace TaikoLocalServer.Services.Interfaces;
using SharedProject.Models;
public interface ISongLeaderboardService
{
public Task<List<SongLeaderboard>> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid, int limit = 10);
}

View File

@ -0,0 +1,97 @@
using GameDatabase.Context;
using SharedProject.Models;
namespace TaikoLocalServer.Services;
public class SongLeaderboardService : ISongLeaderboardService
{
private readonly TaikoDbContext context;
public SongLeaderboardService(TaikoDbContext context)
{
this.context = context;
}
public async Task<List<SongLeaderboard>> GetSongLeaderboard(uint songId, Difficulty difficulty, int baid,
int limit = 10)
{
if (baid == 0)
{
throw new ArgumentNullException(nameof(baid));
}
// get all scores for the song from every user
var scores = await context.SongBestData
.Where(x => x.SongId == songId && x.Difficulty == difficulty)
.OrderByDescending(x => x.BestScore)
.ThenByDescending(x => x.BestRate)
.Take(limit)
.ToListAsync();
// get the user data for each score
var leaderboard = new List<SongLeaderboard>();
// get the user data for each score
foreach (var score in scores)
{
var user = await context.UserData
.Where(x => x.Baid == score.Baid)
.FirstOrDefaultAsync();
leaderboard.Add(new SongLeaderboard
{
Rank = (uint)leaderboard.Count + 1,
Baid = score.Baid,
UserName = user?.MyDonName,
BestScore = score.BestScore,
BestRate = score.BestRate,
BestCrown = score.BestCrown,
BestScoreRank = score.BestScoreRank,
GoodCount = score.GoodCount,
OkCount = score.OkCount,
MissCount = score.MissCount,
ComboCount = score.ComboCount,
HitCount = score.HitCount,
DrumrollCount = score.DrumrollCount
});
}
// get current user score if it exists
var currentUserScore = await context.SongBestData
.Where(x => x.SongId == songId && x.Difficulty == difficulty && x.Baid == baid)
.FirstOrDefaultAsync();
if (currentUserScore != null)
{
// check if they are in the limit
var userRank = leaderboard.FindIndex(x => x.Baid == baid);
if (userRank >= limit)
{
// get the user data for the current user
var user = await context.UserData
.Where(x => x.Baid == baid)
.FirstOrDefaultAsync();
leaderboard.Add(new SongLeaderboard
{
Rank = (uint)userRank + 1,
Baid = currentUserScore.Baid,
UserName = user?.MyDonName,
BestScore = currentUserScore.BestScore,
BestRate = currentUserScore.BestRate,
BestCrown = currentUserScore.BestCrown,
BestScoreRank = currentUserScore.BestScoreRank,
GoodCount = currentUserScore.GoodCount,
OkCount = currentUserScore.OkCount,
MissCount = currentUserScore.MissCount,
ComboCount = currentUserScore.ComboCount,
HitCount = currentUserScore.HitCount,
DrumrollCount = currentUserScore.DrumrollCount
});
}
}
return leaderboard;
}
}

View File

@ -0,0 +1,2 @@
@using TaikoWebUI.Utilities;