diff --git a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs index a996612b..893648e0 100644 --- a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs +++ b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs @@ -12,7 +12,7 @@ namespace LBPUnion.ProjectLighthouse.Tests [Fact] public async Task ShouldReturnErrorOnNoPostData() { - HttpResponseMessage response = await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/login", null!); + HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!); Assert.False(response.IsSuccessStatusCode); #if NET6_0_OR_GREATER Assert.True(response.StatusCode == HttpStatusCode.BadRequest); diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 9035b5a5..9bfc9063 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -103,6 +103,7 @@ True True True + True True True True diff --git a/ProjectLighthouse/Controllers/ScoreController.cs b/ProjectLighthouse/Controllers/ScoreController.cs index 287be62c..90a710ee 100644 --- a/ProjectLighthouse/Controllers/ScoreController.cs +++ b/ProjectLighthouse/Controllers/ScoreController.cs @@ -1,6 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; @@ -30,10 +35,91 @@ namespace LBPUnion.ProjectLighthouse.Controllers score.SlotId = id; - this.database.Scores.Add(score); + IQueryable existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection); + + if (existingScore.Any()) + { + Score first = existingScore.First(s => s.SlotId == score.SlotId); + score.ScoreId = first.ScoreId; + score.Points = Math.Max(first.Points, score.Points); + this.database.Entry(first).CurrentValues.SetValues(score); + } + else + { + this.database.Scores.Add(score); + } await this.database.SaveChangesAsync(); return this.Ok(); } + + [HttpGet("topscores/user/{slotId:int}/{type:int}")] + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public async Task TopScores(int slotId, int type, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + // Get username + User user = await this.database.UserFromRequest(this.Request); + + // This is hella ugly but it technically assigns the proper rank to a score + // var needed for Anonymous type returned from SELECT + var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type) + .OrderByDescending(s => s.Points) + .ToList() + .Select + ( + (Score s, int rank) => new + { + Score = s, + Rank = rank + 1, + } + ); + + // Find your score, since even if you aren't in the top list your score is pinned + var myScore = rankedScores.Where + (rs => rs.Score.PlayerIdCollection.Contains(user.Username)) + .OrderByDescending(rs => rs.Score.Points) + .FirstOrDefault(); + + // Paginated viewing + var pagedScores = rankedScores.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)); + + string serializedScores = pagedScores.Aggregate + ( + string.Empty, + (current, rs) => + { + rs.Score.Rank = rs.Rank; + return current + rs.Score.Serialize(); + } + ); + + string res; + if (myScore == null) + { + res = LbpSerializer.StringElement("scores", serializedScores); + } + else + { + res = LbpSerializer.TaggedStringElement + ( + "scores", + serializedScores, + new Dictionary() + { + { + "yourScore", myScore.Score.Points + }, + { + "yourRank", myScore.Rank + }, //This is the numerator of your position globally in the side menu. + { + "totalNumScores", rankedScores.Count() + }, // This is the denominator of your position globally in the side menu. + } + ); + } + + return this.Ok(res); + } } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs index 4ed898e9..177027de 100644 --- a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs @@ -8,11 +8,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers.Extensions { char[] invalidPathChars = Path.GetInvalidFileNameChars(); string path = text; - - foreach (char c in invalidPathChars) - { - path = path.Replace(c.ToString(), ""); - } + + foreach (char c in invalidPathChars) path = path.Replace(c.ToString(), ""); return path; } diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs index 31879a69..4b0e08a4 100644 --- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs +++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs @@ -9,6 +9,7 @@ namespace LBPUnion.ProjectLighthouse.Logging public class LighthouseFileLogger : LoggerBase { private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs"); + public override bool AllowMultiple => false; public override void Send(LoggerLine line) { @@ -22,6 +23,5 @@ namespace LBPUnion.ProjectLighthouse.Logging File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile); File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll); } - public override bool AllowMultiple => false; } } \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index 64127ba0..37dc28d0 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -8,19 +8,19 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index 9bec4cb2..cfa64535 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -26,6 +26,9 @@ namespace LBPUnion.ProjectLighthouse.Serialization public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; + public static string TaggedStringElement(string key, object value, Dictionary attrKeyValuePairs) + => $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}"; + public static string Elements (params KeyValuePair[] pairs) => pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair)); diff --git a/ProjectLighthouse/Types/Score.cs b/ProjectLighthouse/Types/Score.cs index 14668872..6b507e6f 100644 --- a/ProjectLighthouse/Types/Score.cs +++ b/ProjectLighthouse/Types/Score.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Levels; namespace LBPUnion.ProjectLighthouse.Types @@ -33,7 +34,29 @@ namespace LBPUnion.ProjectLighthouse.Types set => this.PlayerIdCollection = string.Join(',', value); } + [NotMapped] + [XmlElement("mainPlayer")] + public string MainPlayer { + get => this.PlayerIds[0]; + set => this.PlayerIds[0] = value; + } + + [NotMapped] + [XmlElement("rank")] + public int Rank { get; set; } + [XmlElement("score")] public int Points { get; set; } + + public string Serialize() + { + string score = LbpSerializer.StringElement("type", this.Type) + + LbpSerializer.StringElement("playerIds", this.PlayerIdCollection) + + LbpSerializer.StringElement("mainPlayer", this.MainPlayer) + + LbpSerializer.StringElement("rank", this.Rank) + + LbpSerializer.StringElement("score", this.Points); + + return LbpSerializer.StringElement("playRecord", score); + } } } \ No newline at end of file