1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 12:33:01 +08:00

Isolate score submissions model and remove serialisation from ScoreInfo

This commit is contained in:
Dean Herbert 2021-10-29 13:02:19 +09:00
parent 1944c255a7
commit 54073d8a1e
2 changed files with 68 additions and 39 deletions

View File

@ -1,12 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Online.Solo namespace osu.Game.Online.Solo
{ {
@ -16,13 +21,13 @@ namespace osu.Game.Online.Solo
private readonly int beatmapId; private readonly int beatmapId;
private readonly ScoreInfo scoreInfo; private readonly SubmittableScore score;
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo) public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
{ {
this.beatmapId = beatmapId; this.beatmapId = beatmapId;
this.scoreId = scoreId; this.scoreId = scoreId;
this.scoreInfo = scoreInfo; score = new SubmittableScore(scoreInfo);
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -32,7 +37,7 @@ namespace osu.Game.Online.Solo
req.ContentType = "application/json"; req.ContentType = "application/json";
req.Method = HttpMethod.Put; req.Method = HttpMethod.Put;
req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings
{ {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore ReferenceLoopHandling = ReferenceLoopHandling.Ignore
})); }));
@ -42,4 +47,57 @@ namespace osu.Game.Online.Solo
protected override string Target => $@"beatmaps/{beatmapId}/solo/scores/{scoreId}"; protected override string Target => $@"beatmaps/{beatmapId}/solo/scores/{scoreId}";
} }
/// <summary>
/// A class specifically for sending scores to the API during score submission.
/// This is used instead of <see cref="APIScoreInfo"/> due to marginally different serialisation naming requirements.
/// </summary>
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<HitResult, int> 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;
}
}
} }

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
@ -23,44 +22,32 @@ namespace osu.Game.Scoring
{ {
public int ID { get; set; } public int ID { get; set; }
[JsonProperty("rank")]
[JsonConverter(typeof(StringEnumConverter))]
public ScoreRank Rank { get; set; } public ScoreRank Rank { get; set; }
[JsonProperty("total_score")]
public long TotalScore { get; set; } 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. [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; } public double Accuracy { get; set; }
[JsonIgnore]
public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy();
[JsonProperty(@"pp")]
public double? PP { get; set; } public double? PP { get; set; }
[JsonProperty("max_combo")]
public int MaxCombo { get; set; } public int MaxCombo { get; set; }
[JsonIgnore]
public int Combo { get; set; } // Todo: Shouldn't exist in here public int Combo { get; set; } // Todo: Shouldn't exist in here
[JsonProperty("ruleset_id")]
public int RulesetID { get; set; } public int RulesetID { get; set; }
[JsonProperty("passed")]
[NotMapped] [NotMapped]
public bool Passed { get; set; } = true; public bool Passed { get; set; } = true;
[JsonIgnore]
public RulesetInfo Ruleset { get; set; } public RulesetInfo Ruleset { get; set; }
private APIMod[] localAPIMods; private APIMod[] localAPIMods;
private Mod[] mods; private Mod[] mods;
[JsonIgnore]
[NotMapped] [NotMapped]
public Mod[] Mods public Mod[] Mods
{ {
@ -75,7 +62,7 @@ namespace osu.Game.Scoring
if (mods != null) if (mods != null)
scoreMods = mods; scoreMods = mods;
else if (localAPIMods != null) else if (localAPIMods != null)
scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
return scoreMods; return scoreMods;
} }
@ -87,9 +74,8 @@ namespace osu.Game.Scoring
} }
// Used for API serialisation/deserialisation. // Used for API serialisation/deserialisation.
[JsonProperty("mods")]
[NotMapped] [NotMapped]
private APIMod[] apiMods public APIMod[] APIMods
{ {
get get
{ {
@ -111,19 +97,16 @@ namespace osu.Game.Scoring
} }
// Used for database serialisation/deserialisation. // Used for database serialisation/deserialisation.
[JsonIgnore]
[Column("Mods")] [Column("Mods")]
public string ModsJson public string ModsJson
{ {
get => JsonConvert.SerializeObject(apiMods); get => JsonConvert.SerializeObject(APIMods);
set => apiMods = JsonConvert.DeserializeObject<APIMod[]>(value); set => APIMods = JsonConvert.DeserializeObject<APIMod[]>(value);
} }
[NotMapped] [NotMapped]
[JsonProperty("user")]
public User User { get; set; } public User User { get; set; }
[JsonIgnore]
[Column("User")] [Column("User")]
public string UserString public string UserString
{ {
@ -135,7 +118,6 @@ namespace osu.Game.Scoring
} }
} }
[JsonIgnore]
[Column("UserID")] [Column("UserID")]
public int? UserID public int? UserID
{ {
@ -147,23 +129,18 @@ namespace osu.Game.Scoring
} }
} }
[JsonIgnore]
public int BeatmapInfoID { get; set; } public int BeatmapInfoID { get; set; }
[JsonIgnore]
[Column("Beatmap")] [Column("Beatmap")]
public virtual BeatmapInfo BeatmapInfo { get; set; } public BeatmapInfo BeatmapInfo { get; set; }
[JsonIgnore]
public long? OnlineScoreID { get; set; } public long? OnlineScoreID { get; set; }
[JsonIgnore]
public DateTimeOffset Date { get; set; } public DateTimeOffset Date { get; set; }
[JsonProperty("statistics")] [NotMapped]
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>(); public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
[JsonIgnore]
[Column("Statistics")] [Column("Statistics")]
public string StatisticsJson public string StatisticsJson
{ {
@ -181,29 +158,23 @@ namespace osu.Game.Scoring
} }
[NotMapped] [NotMapped]
[JsonIgnore]
public List<HitEvent> HitEvents { get; set; } public List<HitEvent> HitEvents { get; set; }
[JsonIgnore]
public List<ScoreFileInfo> Files { get; set; } public List<ScoreFileInfo> Files { get; set; }
[JsonIgnore]
public string Hash { get; set; } public string Hash { get; set; }
[JsonIgnore]
public bool DeletePending { get; set; } public bool DeletePending { get; set; }
/// <summary> /// <summary>
/// The position of this score, starting at 1. /// The position of this score, starting at 1.
/// </summary> /// </summary>
[NotMapped] [NotMapped]
[JsonProperty("position")] public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone.
public int? Position { get; set; }
/// <summary> /// <summary>
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score. /// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
/// </summary> /// </summary>
[JsonIgnore]
[NotMapped] [NotMapped]
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any(); public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();