Add LBP1 level scoreboards (#728)

* Add missing lbp1 scoreboard endpoint

* Allow all score types to be fetched

* Make lbp1 scores start at highest

* Fix query to use ScoreType from options

* Implement multi-type scoreboard response
This commit is contained in:
Josh 2023-03-31 16:08:27 -05:00 committed by GitHub
parent 5e4954ae32
commit 0253864f5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 18 deletions

View file

@ -30,6 +30,28 @@ public class ScoreController : ControllerBase
this.database = database; this.database = database;
} }
private string[] getFriendUsernames(int userId, string username)
{
UserFriendData? store = UserFriendStore.GetUserFriendData(userId);
if (store == null) return new[] { username, };
List<string> friendNames = new()
{
username,
};
// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (int friendId in store.FriendIds)
{
string? friendUsername = this.database.Users.Where(u => u.UserId == friendId)
.Select(u => u.Username)
.FirstOrDefault();
if (friendUsername != null) friendNames.Add(friendUsername);
}
return friendNames.ToArray();
}
[HttpPost("scoreboard/{slotType}/{id:int}")] [HttpPost("scoreboard/{slotType}/{id:int}")]
[HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")] [HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")]
public async Task<IActionResult> SubmitScore(string slotType, int id, int childId) public async Task<IActionResult> SubmitScore(string slotType, int id, int childId)
@ -165,6 +187,45 @@ public class ScoreController : ControllerBase
})); }));
} }
[HttpGet("scoreboard/{slotType}/{id:int}")]
[HttpPost("scoreboard/friends/{slotType}/{id:int}")]
public async Task<IActionResult> Lbp1Leaderboards(string slotType, int id)
{
GameTokenEntity token = this.GetToken();
string username = await this.database.UsernameFromGameToken(token);
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
LeaderboardOptions options = new()
{
PageSize = 10,
PageStart = 1,
ScoreType = -1,
SlotId = id,
TargetUsername = username,
RootName = "scoreboardSegment",
};
if (!HttpMethods.IsPost(this.Request.Method))
{
List<PlayerScoreboardResponse> scoreboardResponses = new();
for (int i = 1; i <= 4; i++)
{
options.ScoreType = i;
ScoreboardResponse response = this.getScores(options);
scoreboardResponses.Add(new PlayerScoreboardResponse(response.Scores, i));
}
return this.Ok(new MultiScoreboardResponse(scoreboardResponses));
}
GameScore? score = await this.DeserializeBody<GameScore>();
if (score == null) return this.BadRequest();
options.ScoreType = score.Type;
options.TargetPlayerIds = this.getFriendUsernames(token.UserId, username);
return this.Ok(this.getScores(options));
}
[HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")] [HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")]
[HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")] [HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")]
public async Task<IActionResult> FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5) public async Task<IActionResult> FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
@ -179,21 +240,7 @@ public class ScoreController : ControllerBase
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer); if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
UserFriendData? store = UserFriendStore.GetUserFriendData(token.UserId); string[] friendIds = this.getFriendUsernames(token.UserId, username);
if (store == null) return this.Ok();
List<string> friendNames = new()
{
username,
};
foreach (int friendId in store.FriendIds)
{
string? friendUsername = await this.database.Users.Where(u => u.UserId == friendId)
.Select(u => u.Username)
.FirstOrDefaultAsync();
if (friendUsername != null) friendNames.Add(friendUsername);
}
return this.Ok(this.getScores(new LeaderboardOptions return this.Ok(this.getScores(new LeaderboardOptions
{ {
@ -204,7 +251,7 @@ public class ScoreController : ControllerBase
ChildSlotId = childId, ChildSlotId = childId,
ScoreType = type, ScoreType = type,
TargetUsername = username, TargetUsername = username,
TargetPlayerIds = friendNames.ToArray(), TargetPlayerIds = friendIds,
})); }));
} }
@ -250,10 +297,10 @@ public class ScoreController : ControllerBase
private ScoreboardResponse getScores(LeaderboardOptions options) private ScoreboardResponse getScores(LeaderboardOptions options)
{ {
// This is hella ugly but it technically assigns the proper rank to a score // This is hella ugly but it technically assigns the proper rank to a score
// var needed for Anonymous type returned from SELECT // var needed for Anonymous type returned from SELECT
var rankedScores = this.database.Scores.Where(s => s.SlotId == options.SlotId && s.Type == options.ScoreType) var rankedScores = this.database.Scores.Where(s => s.SlotId == options.SlotId)
.Where(s => options.ScoreType == -1 || s.Type == options.ScoreType)
.Where(s => s.ChildSlotId == 0 || s.ChildSlotId == options.ChildSlotId) .Where(s => s.ChildSlotId == 0 || s.ChildSlotId == options.ChildSlotId)
.AsEnumerable() .AsEnumerable()
.Where(s => options.TargetPlayerIds == null || .Where(s => options.TargetPlayerIds == null ||

View file

@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace LBPUnion.ProjectLighthouse.Types.Serialization;
[XmlRoot("scoreboards")]
public class MultiScoreboardResponse : ILbpSerializable
{
public MultiScoreboardResponse() { }
public MultiScoreboardResponse(List<PlayerScoreboardResponse> scoreboards)
{
this.Scoreboards = scoreboards;
}
[XmlElement("topScores")]
public List<PlayerScoreboardResponse> Scoreboards { get; set; }
}
public class PlayerScoreboardResponse : ILbpSerializable
{
public PlayerScoreboardResponse() { }
public PlayerScoreboardResponse(List<GameScore> scores, int playerCount, int firstRank = 1)
{
this.Scores = scores;
this.PlayerCount = playerCount;
this.FirstRank = firstRank;
}
[XmlElement("playRecord")]
public List<GameScore> Scores { get; set; }
[XmlAttribute("players")]
public int PlayerCount { get; set; }
[XmlAttribute("firstRank")]
public int FirstRank { get; set; }
}