From 54073d8a1e4b6f9294590c37b54c0eb87bd89b9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 13:02:19 +0900 Subject: [PATCH] Isolate score submissions model and remove serialisation from `ScoreInfo` --- .../Online/Solo/SubmitSoloScoreRequest.cs | 64 ++++++++++++++++++- osu.Game/Scoring/ScoreInfo.cs | 43 ++----------- 2 files changed, 68 insertions(+), 39 deletions(-) diff --git a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs index 85fa3eeb34..184dbc911d 100644 --- a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs +++ b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs @@ -1,12 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Net.Http; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Framework.IO.Network; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Online.Solo { @@ -16,13 +21,13 @@ namespace osu.Game.Online.Solo private readonly int beatmapId; - private readonly ScoreInfo scoreInfo; + private readonly SubmittableScore score; public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo) { this.beatmapId = beatmapId; this.scoreId = scoreId; - this.scoreInfo = scoreInfo; + score = new SubmittableScore(scoreInfo); } protected override WebRequest CreateWebRequest() @@ -32,7 +37,7 @@ namespace osu.Game.Online.Solo req.ContentType = "application/json"; req.Method = HttpMethod.Put; - req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings + req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })); @@ -42,4 +47,57 @@ namespace osu.Game.Online.Solo protected override string Target => $@"beatmaps/{beatmapId}/solo/scores/{scoreId}"; } + + /// + /// A class specifically for sending scores to the API during score submission. + /// This is used instead of due to marginally different serialisation naming requirements. + /// + public class SubmittableScore + { + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; } + + [JsonProperty("total_score")] + public long TotalScore { get; } + + [JsonProperty("accuracy")] + public double Accuracy { get; } + + [JsonProperty(@"pp")] + public double? PP { get; } + + [JsonProperty("max_combo")] + public int MaxCombo { get; } + + [JsonProperty("ruleset_id")] + public int RulesetID { get; } + + [JsonProperty("passed")] + public bool Passed { get; } + + // Used for API serialisation/deserialisation. + [JsonProperty("mods")] + public APIMod[] Mods { get; } + + [JsonProperty("user")] + public User User { get; } + + [JsonProperty("statistics")] + public Dictionary Statistics { get; } + + public SubmittableScore(ScoreInfo score) + { + Rank = score.Rank; + TotalScore = score.TotalScore; + Accuracy = score.Accuracy; + PP = score.PP; + MaxCombo = score.MaxCombo; + RulesetID = score.RulesetID; + Passed = score.Passed; + Mods = score.APIMods; + User = score.User; + Statistics = score.Statistics; + } + } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 3b09669926..2747cd48c0 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Database; @@ -23,44 +22,32 @@ namespace osu.Game.Scoring { public int ID { get; set; } - [JsonProperty("rank")] - [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } - [JsonProperty("total_score")] public long TotalScore { get; set; } - [JsonProperty("accuracy")] [Column(TypeName = "DECIMAL(1,4)")] // TODO: This data type is wrong (should contain more precision). But at the same time, we probably don't need to be storing this in the database. public double Accuracy { get; set; } - [JsonIgnore] public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); - [JsonProperty(@"pp")] public double? PP { get; set; } - [JsonProperty("max_combo")] public int MaxCombo { get; set; } - [JsonIgnore] public int Combo { get; set; } // Todo: Shouldn't exist in here - [JsonProperty("ruleset_id")] public int RulesetID { get; set; } - [JsonProperty("passed")] [NotMapped] public bool Passed { get; set; } = true; - [JsonIgnore] public RulesetInfo Ruleset { get; set; } private APIMod[] localAPIMods; private Mod[] mods; - [JsonIgnore] [NotMapped] public Mod[] Mods { @@ -75,7 +62,7 @@ namespace osu.Game.Scoring if (mods != null) scoreMods = mods; else if (localAPIMods != null) - scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); + scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); return scoreMods; } @@ -87,9 +74,8 @@ namespace osu.Game.Scoring } // Used for API serialisation/deserialisation. - [JsonProperty("mods")] [NotMapped] - private APIMod[] apiMods + public APIMod[] APIMods { get { @@ -111,19 +97,16 @@ namespace osu.Game.Scoring } // Used for database serialisation/deserialisation. - [JsonIgnore] [Column("Mods")] public string ModsJson { - get => JsonConvert.SerializeObject(apiMods); - set => apiMods = JsonConvert.DeserializeObject(value); + get => JsonConvert.SerializeObject(APIMods); + set => APIMods = JsonConvert.DeserializeObject(value); } [NotMapped] - [JsonProperty("user")] public User User { get; set; } - [JsonIgnore] [Column("User")] public string UserString { @@ -135,7 +118,6 @@ namespace osu.Game.Scoring } } - [JsonIgnore] [Column("UserID")] public int? UserID { @@ -147,23 +129,18 @@ namespace osu.Game.Scoring } } - [JsonIgnore] public int BeatmapInfoID { get; set; } - [JsonIgnore] [Column("Beatmap")] - public virtual BeatmapInfo BeatmapInfo { get; set; } + public BeatmapInfo BeatmapInfo { get; set; } - [JsonIgnore] public long? OnlineScoreID { get; set; } - [JsonIgnore] public DateTimeOffset Date { get; set; } - [JsonProperty("statistics")] + [NotMapped] public Dictionary Statistics { get; set; } = new Dictionary(); - [JsonIgnore] [Column("Statistics")] public string StatisticsJson { @@ -181,29 +158,23 @@ namespace osu.Game.Scoring } [NotMapped] - [JsonIgnore] public List HitEvents { get; set; } - [JsonIgnore] public List Files { get; set; } - [JsonIgnore] public string Hash { get; set; } - [JsonIgnore] public bool DeletePending { get; set; } /// /// The position of this score, starting at 1. /// [NotMapped] - [JsonProperty("position")] - public int? Position { get; set; } + public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone. /// /// Whether this represents a legacy (osu!stable) score. /// - [JsonIgnore] [NotMapped] public bool IsLegacyScore => Mods.OfType().Any();