From 2e96f74c94a008e62a523e9ca15cf0b21ba05a71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Feb 2022 16:37:14 +0900 Subject: [PATCH 1/4] Allow `LegacyScoreEncoder` to be used without a beatmap if frames are already legacy frames --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 9460ec680c..14a6495282 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -5,9 +5,11 @@ using System; using System.IO; using System.Linq; using System.Text; +using JetBrains.Annotations; using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO.Legacy; +using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; using SharpCompress.Compressors.LZMA; @@ -29,12 +31,21 @@ namespace osu.Game.Scoring.Legacy private readonly Score score; private readonly IBeatmap beatmap; - public LegacyScoreEncoder(Score score, IBeatmap beatmap) + /// + /// Create a new score encoder for a specific score. + /// + /// The score to be encoded. + /// The beatmap used to convert frames for the score. May be null if the frames are already s. + /// + public LegacyScoreEncoder(Score score, [CanBeNull] IBeatmap beatmap) { this.score = score; this.beatmap = beatmap; - if (score.ScoreInfo.BeatmapInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.BeatmapInfo.Ruleset.OnlineID > 3) + if (beatmap == null && !score.Replay.Frames.All(f => f is LegacyReplayFrame)) + throw new ArgumentException("Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap)); + + if (score.ScoreInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.Ruleset.OnlineID > 3) throw new ArgumentException("Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score)); } From 723e96309ae8b75b3a89a940ddebeef8e0ea45cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Feb 2022 16:40:00 +0900 Subject: [PATCH 2/4] Only convert non-legacy frames (and throw on conversion failure) --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 14a6495282..3355efc830 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -10,6 +10,7 @@ using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO.Legacy; using osu.Game.Replays.Legacy; +using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using SharpCompress.Compressors.LZMA; @@ -112,11 +113,16 @@ namespace osu.Game.Scoring.Legacy { int lastTime = 0; - foreach (var f in score.Replay.Frames.OfType().Select(f => f.ToLegacy(beatmap))) + foreach (var f in score.Replay.Frames) { + var legacyFrame = getLegacyFrame(f); + + if (legacyFrame == null) + throw new ArgumentException("One or more frames could not be converted to legacy frames"); + // Rounding because stable could only parse integral values - int time = (int)Math.Round(f.Time); - replayData.Append(FormattableString.Invariant($"{time - lastTime}|{f.MouseX ?? 0}|{f.MouseY ?? 0}|{(int)f.ButtonState},")); + int time = (int)Math.Round(legacyFrame.Time); + replayData.Append(FormattableString.Invariant($"{time - lastTime}|{legacyFrame.MouseX ?? 0}|{legacyFrame.MouseY ?? 0}|{(int)legacyFrame.ButtonState},")); lastTime = time; } } @@ -128,6 +134,14 @@ namespace osu.Game.Scoring.Legacy } } + private LegacyReplayFrame getLegacyFrame(ReplayFrame replayFrame) + { + if (replayFrame is LegacyReplayFrame legacyFrame) + return legacyFrame; + + return (replayFrame as IConvertibleReplayFrame)?.ToLegacy(beatmap); + } + private string getHpGraphFormatted() { // todo: implement, maybe? From 52e50db6b94ca11aa8df4ca2b14dbbb39c68ac47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Feb 2022 16:41:39 +0900 Subject: [PATCH 3/4] Enable `nullable` for `LegacyScoreEncoder` --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 3355efc830..1e39b66ead 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Linq; using System.Text; -using JetBrains.Annotations; using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO.Legacy; @@ -14,6 +13,8 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using SharpCompress.Compressors.LZMA; +#nullable enable + namespace osu.Game.Scoring.Legacy { public class LegacyScoreEncoder @@ -30,7 +31,7 @@ namespace osu.Game.Scoring.Legacy public const int FIRST_LAZER_VERSION = 30000000; private readonly Score score; - private readonly IBeatmap beatmap; + private readonly IBeatmap? beatmap; /// /// Create a new score encoder for a specific score. @@ -38,16 +39,16 @@ namespace osu.Game.Scoring.Legacy /// The score to be encoded. /// The beatmap used to convert frames for the score. May be null if the frames are already s. /// - public LegacyScoreEncoder(Score score, [CanBeNull] IBeatmap beatmap) + public LegacyScoreEncoder(Score score, IBeatmap? beatmap) { this.score = score; this.beatmap = beatmap; if (beatmap == null && !score.Replay.Frames.All(f => f is LegacyReplayFrame)) - throw new ArgumentException("Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap)); + throw new ArgumentException(@"Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap)); if (score.ScoreInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.Ruleset.OnlineID > 3) - throw new ArgumentException("Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score)); + throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score)); } public void Encode(Stream stream) @@ -117,9 +118,6 @@ namespace osu.Game.Scoring.Legacy { var legacyFrame = getLegacyFrame(f); - if (legacyFrame == null) - throw new ArgumentException("One or more frames could not be converted to legacy frames"); - // Rounding because stable could only parse integral values int time = (int)Math.Round(legacyFrame.Time); replayData.Append(FormattableString.Invariant($"{time - lastTime}|{legacyFrame.MouseX ?? 0}|{legacyFrame.MouseY ?? 0}|{(int)legacyFrame.ButtonState},")); @@ -136,10 +134,17 @@ namespace osu.Game.Scoring.Legacy private LegacyReplayFrame getLegacyFrame(ReplayFrame replayFrame) { - if (replayFrame is LegacyReplayFrame legacyFrame) - return legacyFrame; + switch (replayFrame) + { + case LegacyReplayFrame legacyFrame: + return legacyFrame; - return (replayFrame as IConvertibleReplayFrame)?.ToLegacy(beatmap); + case IConvertibleReplayFrame convertibleFrame: + return convertibleFrame.ToLegacy(beatmap); + + default: + throw new ArgumentException(@"Frame could not be converted to legacy frames", nameof(replayFrame)); + } } private string getHpGraphFormatted() From eb75a29b2024b1001001fe4ef7b523624e33ef3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 1 Mar 2022 12:07:03 +0900 Subject: [PATCH 4/4] Use constant for maximum legacy ruleset id --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 1e39b66ead..1326395695 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO.Legacy; using osu.Game.Replays.Legacy; +using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using SharpCompress.Compressors.LZMA; @@ -47,7 +48,7 @@ namespace osu.Game.Scoring.Legacy if (beatmap == null && !score.Replay.Frames.All(f => f is LegacyReplayFrame)) throw new ArgumentException(@"Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap)); - if (score.ScoreInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.Ruleset.OnlineID > 3) + if (score.ScoreInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.Ruleset.OnlineID > ILegacyRuleset.MAX_LEGACY_RULESET_ID) throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score)); }