From a3c7755ade849b1e99bfea39d3e53dc108f56746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 28 Feb 2018 16:34:47 +0900 Subject: [PATCH 01/18] Implement a conversion process for ReplayFrames --- .../Replays/CatchAutoGenerator.cs | 6 +- .../Replays/CatchFramedReplayInputHandler.cs | 32 ++-- .../Replays/CatchReplayFrame.cs | 22 ++- .../UI/CatchRulesetContainer.cs | 3 +- .../Beatmaps/StageDefinition.cs | 12 ++ .../Mods/ManiaModAutoplay.cs | 3 +- .../Replays/ManiaAutoGenerator.cs | 33 +++- .../Replays/ManiaFramedReplayInputHandler.cs | 30 +--- .../Replays/ManiaReplayFrame.cs | 45 +++++- .../Tests/TestCaseAutoGeneration.cs | 60 +++---- .../UI/ManiaRulesetContainer.cs | 3 +- osu.Game.Rulesets.Mania/UI/ManiaStage.cs | 11 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 + .../Replays/OsuAutoGenerator.cs | 71 ++++---- .../Replays/OsuReplayFrame.cs | 38 +++++ .../Replays/OsuReplayInputHandler.cs | 21 ++- .../UI/OsuRulesetContainer.cs | 3 +- .../osu.Game.Rulesets.Osu.csproj | 1 + .../Replays/TaikoAutoGenerator.cs | 42 ++--- .../Replays/TaikoFramedReplayInputHandler.cs | 18 +-- .../Replays/TaikoReplayFrame.cs | 22 ++- .../UI/TaikoRulesetContainer.cs | 3 +- .../Replays/FramedReplayInputHandler.cs | 47 ++---- .../Replays/Legacy/LegacyReplayFrame.cs | 62 +++++++ .../Replays/{ => Legacy}/ReplayButtonState.cs | 2 +- osu.Game/Rulesets/Replays/Replay.cs | 1 - osu.Game/Rulesets/Replays/ReplayFrame.cs | 55 +------ .../Replays/Types/IConvertibleReplayFrame.cs | 14 ++ osu.Game/Rulesets/Ruleset.cs | 3 + osu.Game/Rulesets/Scoring/IScoreParser.cs | 12 ++ .../Scoring/Legacy/LegacyScoreParser.cs | 151 ++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreStore.cs | 127 +-------------- osu.Game/Rulesets/UI/RulesetContainer.cs | 3 +- osu.Game/osu.Game.csproj | 10 +- 34 files changed, 577 insertions(+), 393 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs create mode 100644 osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs rename osu.Game/Rulesets/Replays/{ => Legacy}/ReplayButtonState.cs (85%) create mode 100644 osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs create mode 100644 osu.Game/Rulesets/Scoring/IScoreParser.cs create mode 100644 osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index f8ca75fae9..d0772bbaa1 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Replays } else if (h.HyperDash) { - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, ReplayButtonState.Right1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, true)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } else if (dashRequired) @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Replays float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable); //dash movement - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, ReplayButtonState.Left1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.Replays { double timeBefore = positionChange / movement_speed; - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, ReplayButtonState.Right1)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, true)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 2f296a2504..795a859f5f 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -3,37 +3,49 @@ using System.Collections.Generic; using osu.Framework.Input; +using osu.Framework.MathUtils; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Catch.Replays { - public class CatchFramedReplayInputHandler : FramedReplayInputHandler + public class CatchFramedReplayInputHandler : FramedReplayInputHandler { public CatchFramedReplayInputHandler(Replay replay) : base(replay) { } + protected float? Position + { + get + { + if (!HasFrames) + return null; + + return Interpolation.ValueAt(CurrentTime, CurrentFrame.X, NextFrame.X, CurrentFrame.Time, NextFrame.Time); + } + } + public override List GetPendingStates() { if (!Position.HasValue) return new List(); - var action = new List(); + var actions = new List(); - if (CurrentFrame.ButtonState == ReplayButtonState.Left1) - action.Add(CatchAction.Dash); + if (CurrentFrame.Dashing) + actions.Add(CatchAction.Dash); - if (Position.Value.X > CurrentFrame.Position.X) - action.Add(CatchAction.MoveRight); - else if (Position.Value.X < CurrentFrame.Position.X) - action.Add(CatchAction.MoveLeft); + if (Position.Value > CurrentFrame.X) + actions.Add(CatchAction.MoveRight); + else if (Position.Value < CurrentFrame.X) + actions.Add(CatchAction.MoveLeft); return new List { new CatchReplayState { - PressedActions = action, - CatcherX = Position.Value.X + PressedActions = actions, + CatcherX = Position.Value }, }; } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index 0194fc93a4..c81b095f30 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -1,17 +1,31 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Replays { - public class CatchReplayFrame : ReplayFrame + public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame { - public override bool IsImportant => MouseX > 0; + public float X; + public bool Dashing; - public CatchReplayFrame(double time, float? x = null, ReplayButtonState button = ReplayButtonState.None) - : base(time, x ?? -1, null, button) + public CatchReplayFrame(double time, float? x = null, bool dashing = false) + : base(time) { + X = x ?? -1; + Dashing = dashing; + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + { + // Todo: This needs to be re-scaled + X = legacyFrame.Position.X; + Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 956a524121..41dd7fdf4e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -3,6 +3,7 @@ using osu.Framework.Input; using osu.Game.Beatmaps; +using osu.Game.Input.Handlers; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); - protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs index aeefc2f396..113f4f2164 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs @@ -14,5 +14,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// The number of s which this stage contains. /// public int Columns; + + /// + /// Whether this stage has a special column. + /// + public bool HasSpecialColumn => Columns % 2 == 1; + + /// + /// Whether the column index is a special column for this stage. + /// + /// The 0-based column index. + /// Whether the column is a special column. + public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs index 3c5179cef0..9ceb0ab7ea 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mods; @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods return new Score { User = new User { Username = "osu!topus!" }, - Replay = new ManiaAutoGenerator(beatmap).Generate(), + Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), }; } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 6f6217f540..5a992bb970 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -15,10 +15,31 @@ namespace osu.Game.Rulesets.Mania.Replays { public const double RELEASE_DELAY = 20; - public ManiaAutoGenerator(Beatmap beatmap) + public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; + + private readonly ManiaAction[] columnActions; + + public ManiaAutoGenerator(ManiaBeatmap beatmap) : base(beatmap) { Replay = new Replay { User = new User { Username = @"Autoplay" } }; + + columnActions = new ManiaAction[Beatmap.TotalColumns]; + + var normalAction = ManiaAction.Key1; + var specialAction = ManiaAction.Special1; + int totalCounter = 0; + foreach (var stage in Beatmap.Stages) + { + for (int i = 0; i < stage.Columns; i++) + { + if (stage.IsSpecialColumn(i)) + columnActions[totalCounter] = specialAction++; + else + columnActions[totalCounter] = normalAction++; + totalCounter++; + } + } } protected Replay Replay; @@ -30,18 +51,18 @@ namespace osu.Game.Rulesets.Mania.Replays var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); - int activeColumns = 0; + var actions = new List(); foreach (var group in pointGroups) { foreach (var point in group) { if (point is HitPoint) - activeColumns |= 1 << point.Column; + actions.Add(columnActions[point.Column]); if (point is ReleasePoint) - activeColumns ^= 1 << point.Column; + actions.Remove(columnActions[point.Column]); } - Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns)); + Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray())); } return Replay; diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs index fd084f138f..fb9ae37831 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs @@ -2,42 +2,18 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using osu.Framework.Input; -using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Mania.Replays { - internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler + internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler { - private readonly ManiaRulesetContainer container; - - public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container) + public ManiaFramedReplayInputHandler(Replay replay) : base(replay) { - this.container = container; } - private ManiaPlayfield playfield; - public override List GetPendingStates() - { - var actions = new List(); - - if (playfield == null) - playfield = (ManiaPlayfield)container.Playfield; - - int activeColumns = (int)(CurrentFrame.MouseX ?? 0); - int counter = 0; - while (activeColumns > 0) - { - if ((activeColumns & 1) > 0) - actions.Add(playfield.Columns.ElementAt(counter).Action); - counter++; - activeColumns >>= 1; - } - - return new List { new ReplayState { PressedActions = actions } }; - } + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index e5c5ac9eeb..ed1143d8ff 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -1,17 +1,54 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Replays { - public class ManiaReplayFrame : ReplayFrame + public class ManiaReplayFrame : ReplayFrame, IConvertibleReplayFrame { - public override bool IsImportant => MouseX > 0; + public List Actions = new List(); - public ManiaReplayFrame(double time, int activeColumns) - : base(time, activeColumns, null, ReplayButtonState.None) + public ManiaReplayFrame(double time, params ManiaAction[] actions) + : base(time) { + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + { + // We don't need to fully convert, just create the converter + var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.Ruleset.Equals(score.Ruleset), beatmap); + + // Todo: Apply mods to converter + // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling + // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. + + bool isSpecialColumn(int column) => converter.TargetColumns % 2 == 1 && column == converter.TargetColumns / 2; + + var normalAction = ManiaAction.Key1; + var specialAction = ManiaAction.Special1; + + int activeColumns = (int)(legacyFrame.MouseX ?? 0); + int counter = 0; + while (activeColumns > 0) + { + Actions.Add((activeColumns & 1) > 0 ? specialAction : normalAction); + + if (isSpecialColumn(counter)) + normalAction++; + else + specialAction++; + + counter++; + activeColumns >>= 1; + } } } } diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs index 07fb6ac670..5a5c47bc87 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using NUnit.Framework; -using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests @@ -18,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | // | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); var generated = new ManiaAutoGenerator(beatmap).Generate(); @@ -26,8 +28,8 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); - Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); } [Test] @@ -39,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | // | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); var generated = new ManiaAutoGenerator(beatmap).Generate(); @@ -47,8 +49,8 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); - Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released"); } [Test] @@ -58,7 +60,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | - | // | | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); @@ -67,8 +69,8 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); - Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); } [Test] @@ -80,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | * | // | | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); @@ -89,8 +91,8 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); - Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); - Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released"); } [Test] @@ -101,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | - | | // | | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); @@ -112,10 +114,10 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); - Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released"); - Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed"); - Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); } [Test] @@ -128,7 +130,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | | // | | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); @@ -139,10 +141,11 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); - Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); - Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed"); - Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released"); - Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released"); + Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released"); } [Test] @@ -154,7 +157,7 @@ namespace osu.Game.Rulesets.Mania.Tests // | * | | // | | | - var beatmap = new Beatmap(); + var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); @@ -164,9 +167,12 @@ namespace osu.Game.Rulesets.Mania.Tests Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); - Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); - Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed"); - Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released"); + Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released"); + Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed"); + Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released"); } + + private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action)); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 732d5f4109..3ecfee1e8c 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; +using osu.Game.Input.Handlers; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Mods; @@ -103,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f); - protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant); } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index ebd73d7dca..2b8039f5df 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -48,13 +48,11 @@ namespace osu.Game.Rulesets.Mania.UI private Color4 specialColumnColour; private readonly int firstColumnIndex; - private readonly StageDefinition definition; public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) : base(ScrollingDirection.Up) { this.firstColumnIndex = firstColumnIndex; - this.definition = definition; Name = "Stage"; @@ -131,7 +129,7 @@ namespace osu.Game.Rulesets.Mania.UI for (int i = 0; i < definition.Columns; i++) { - var isSpecial = isSpecialColumn(i); + var isSpecial = definition.IsSpecialColumn(i); var column = new Column { IsSpecial = isSpecial, @@ -160,13 +158,6 @@ namespace osu.Game.Rulesets.Mania.UI AddNested(c); } - /// - /// Whether the column index is a special column for this playfield. - /// - /// The 0-based column index. - /// Whether the column is a special column. - private bool isSpecialColumn(int column) => definition.Columns % 2 == 1 && column == definition.Columns / 2; - public override void Add(DrawableHitObject h) { var maniaObject = (ManiaHitObject)h.HitObject; diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index b38f95694f..42ead91f0d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -19,6 +19,8 @@ using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu { @@ -145,6 +147,8 @@ namespace osu.Game.Rulesets.Osu public override int LegacyID => 0; + public override ReplayFrame CreateReplayFrame() => new OsuReplayFrame(); + public OsuRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) { diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 274f7bff62..7aa4108428 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -6,7 +6,7 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using System; -using System.Diagnostics; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Replays; @@ -64,9 +64,9 @@ namespace osu.Game.Rulesets.Osu.Replays { buttonIndex = 0; - AddFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None)); - AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None)); - AddFrameToReplay(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None)); + AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500))); + AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); + AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500))); for (int i = 0; i < Beatmap.HitObjects.Count; i++) { @@ -91,18 +91,18 @@ namespace osu.Game.Rulesets.Osu.Replays // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); + if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); } } @@ -118,9 +118,9 @@ namespace osu.Game.Rulesets.Osu.Replays // TODO: Shouldn't the spinner always spin in the same direction? if (h is Spinner) { - calcSpinnerStartPosAndDirection(Frames[Frames.Count - 1].Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = SPINNER_CENTRE - Frames[Frames.Count - 1].Position; + Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -192,13 +192,13 @@ namespace osu.Game.Rulesets.Osu.Replays private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing) { - ReplayFrame lastFrame = Frames[Frames.Count - 1]; + OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1]; // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); if (waitTime > lastFrame.Time) { - lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState); + lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; AddFrameToReplay(lastFrame); } @@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Replays for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay) { Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing); - AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState)); + AddFrameToReplay(new OsuReplayFrame((int)time, new Vector2(currentPosition.X, currentPosition.Y)) { Actions = lastFrame.Actions }); } buttonIndex = 0; @@ -231,14 +231,14 @@ namespace osu.Game.Rulesets.Osu.Replays { // Time to insert the first frame which clicks the object // Here we mainly need to determine which button to use - ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1; + var action = buttonIndex % 2 == 0 ? OsuAction.LeftButton : OsuAction.RightButton; - ReplayFrame startFrame = new ReplayFrame(h.StartTime, startPosition.X, startPosition.Y, button); + var startFrame = new OsuReplayFrame(h.StartTime, new Vector2(startPosition.X, startPosition.Y), action); // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; int endDelay = h is Spinner ? 1 : 0; - ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None); + var endFrame = new OsuReplayFrame(hEndTime + endDelay, new Vector2(h.StackedEndPosition.X, h.StackedEndPosition.Y)); // Decrement because we want the previous frame, not the next one int index = FindInsertionIndex(startFrame) - 1; @@ -248,19 +248,18 @@ namespace osu.Game.Rulesets.Osu.Replays // Do we have a previous frame? No need to check for < replay.Count since we decremented! if (index >= 0) { - ReplayFrame previousFrame = Frames[index]; - var previousButton = previousFrame.ButtonState; + var previousFrame = (OsuReplayFrame)Frames[index]; + var previousActions = previousFrame.Actions; // If a button is already held, then we simply alternate - if (previousButton != ReplayButtonState.None) + if (previousActions.Any()) { - Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1), "Previous button state was not Left1 nor Right1 despite only using those two states."); - // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button. - if (previousButton == button) + if (previousActions.Contains(action)) { - button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button; - startFrame.ButtonState = button; + action = action == OsuAction.LeftButton ? OsuAction.RightButton : OsuAction.LeftButton; + startFrame.Actions.Clear(); + startFrame.Actions.Add(action); } // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists. @@ -272,9 +271,14 @@ namespace osu.Game.Rulesets.Osu.Replays // After alternating we need to keep holding the other button in the future rather than the previous one. for (int j = index + 1; j < Frames.Count; ++j) { + var frame = (OsuReplayFrame)Frames[j]; + // Don't affect frames which stop pressing a button! - if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton) - Frames[j].ButtonState = button; + if (j < Frames.Count - 1 || frame.Actions.SequenceEqual(previousActions)) + { + frame.Actions.Clear(); + frame.Actions.Add(action); + } } } } @@ -298,16 +302,15 @@ namespace osu.Game.Rulesets.Osu.Replays t = ApplyModsToTime(j - h.StartTime) * spinnerDirection; Vector2 pos = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); - AddFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button)); + AddFrameToReplay(new OsuReplayFrame((int)j, new Vector2(pos.X, pos.Y), action)); } t = ApplyModsToTime(s.EndTime - h.StartTime) * spinnerDirection; Vector2 endPosition = SPINNER_CENTRE + CirclePosition(t / 20 + angle, SPIN_RADIUS); - AddFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button)); + AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(endPosition.X, endPosition.Y), action)); - endFrame.MouseX = endPosition.X; - endFrame.MouseY = endPosition.Y; + endFrame.Position = endPosition; } else if (h is Slider) { @@ -316,10 +319,10 @@ namespace osu.Game.Rulesets.Osu.Replays for (double j = FrameDelay; j < s.Duration; j += FrameDelay) { Vector2 pos = s.StackedPositionAt(j / s.Duration); - AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); + AddFrameToReplay(new OsuReplayFrame(h.StartTime + j, new Vector2(pos.X, pos.Y), action)); } - AddFrameToReplay(new ReplayFrame(s.EndTime, s.StackedEndPosition.X, s.StackedEndPosition.Y, button)); + AddFrameToReplay(new OsuReplayFrame(s.EndTime, new Vector2(s.StackedEndPosition.X, s.StackedEndPosition.Y), action)); } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs new file mode 100644 index 0000000000..5d0ff25109 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public class OsuReplayFrame : ReplayFrame, IConvertibleReplayFrame + { + public Vector2 Position; + public List Actions = new List(); + + public OsuReplayFrame() + : base(0) + { + } + + public OsuReplayFrame(double time, Vector2 position, params OsuAction[] actions) + : base(time) + { + Position = position; + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + { + Position = legacyFrame.Position; + if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); + if (legacyFrame.MouseRight) Actions.Add(OsuAction.RightButton); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs index 63c9111190..aef02dad7e 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -3,31 +3,38 @@ using System.Collections.Generic; using osu.Framework.Input; +using osu.Framework.MathUtils; using osu.Game.Rulesets.Replays; using OpenTK; namespace osu.Game.Rulesets.Osu.Replays { - public class OsuReplayInputHandler : FramedReplayInputHandler + public class OsuReplayInputHandler : FramedReplayInputHandler { public OsuReplayInputHandler(Replay replay) : base(replay) { } + protected Vector2? Position + { + get + { + if (!HasFrames) + return null; + + return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); + } + } + public override List GetPendingStates() { - List actions = new List(); - - if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton); - if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton); - return new List { new ReplayState { Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)), - PressedActions = actions + PressedActions = CurrentFrame.Actions } }; } diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 2af381dd71..b825ba73b7 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using OpenTK; using osu.Game.Beatmaps; +using osu.Game.Input.Handlers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; @@ -48,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI return null; } - protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); protected override Vector2 GetAspectAdjustedSize() { diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 04903d11bf..a42dbeeb10 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -122,6 +122,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index cb45ce2dce..1a556fe01d 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -35,15 +35,13 @@ namespace osu.Game.Rulesets.Taiko.Replays { bool hitButton = true; - Frames.Add(new TaikoReplayFrame(-100000, ReplayButtonState.None)); - Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, ReplayButtonState.None)); + Frames.Add(new TaikoReplayFrame(-100000)); + Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000)); for (int i = 0; i < Beatmap.HitObjects.Count; i++) { TaikoHitObject h = Beatmap.HitObjects[i]; - ReplayButtonState button; - IHasEndTime endTimeData = h as IHasEndTime; double endTime = endTimeData?.EndTime ?? h.StartTime; @@ -59,24 +57,26 @@ namespace osu.Game.Rulesets.Taiko.Replays double hitRate = Math.Min(swell_hit_speed, swell.Duration / req); for (double j = h.StartTime; j < endTime; j += hitRate) { + TaikoAction action; + switch (d) { default: case 0: - button = ReplayButtonState.Left1; + action = TaikoAction.LeftCentre; break; case 1: - button = ReplayButtonState.Right1; + action = TaikoAction.LeftRim; break; case 2: - button = ReplayButtonState.Left2; + action = TaikoAction.RightCentre; break; case 3: - button = ReplayButtonState.Right2; + action = TaikoAction.RightRim; break; } - Frames.Add(new TaikoReplayFrame(j, button)); + Frames.Add(new TaikoReplayFrame(j, action)); d = (d + 1) % 4; if (++count == req) break; @@ -86,39 +86,39 @@ namespace osu.Game.Rulesets.Taiko.Replays { foreach (var tick in drumRoll.NestedHitObjects.OfType()) { - Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); + Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre)); hitButton = !hitButton; } } else if (hit != null) { + TaikoAction[] actions; + if (hit is CentreHit) { - if (h.IsStrong) - button = ReplayButtonState.Left1 | ReplayButtonState.Left2; - else - button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2; + actions = h.IsStrong + ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } + : new[] { hitButton ? TaikoAction.LeftCentre : TaikoAction.RightCentre }; } else { - if (h.IsStrong) - button = ReplayButtonState.Right1 | ReplayButtonState.Right2; - else - button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2; + actions = h.IsStrong + ? new[] { TaikoAction.LeftRim, TaikoAction.RightRim } + : new[] { hitButton ? TaikoAction.LeftRim : TaikoAction.RightRim }; } - Frames.Add(new TaikoReplayFrame(h.StartTime, button)); + Frames.Add(new TaikoReplayFrame(h.StartTime, actions)); } else throw new InvalidOperationException("Unknown hit object type."); - Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY, ReplayButtonState.None)); + Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY)); if (i < Beatmap.HitObjects.Count - 1) { double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000; if (waitTime > endTime) - Frames.Add(new TaikoReplayFrame(waitTime, ReplayButtonState.None)); + Frames.Add(new TaikoReplayFrame(waitTime)); } hitButton = !hitButton; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index 1a96b26d34..1c525dcda0 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -7,27 +7,13 @@ using osu.Framework.Input; namespace osu.Game.Rulesets.Taiko.Replays { - internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler + internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler { public TaikoFramedReplayInputHandler(Replay replay) : base(replay) { } - public override List GetPendingStates() - { - var actions = new List(); - - if (CurrentFrame?.MouseRight1 == true) - actions.Add(TaikoAction.LeftRim); - if (CurrentFrame?.MouseRight2 == true) - actions.Add(TaikoAction.RightRim); - if (CurrentFrame?.MouseLeft1 == true) - actions.Add(TaikoAction.LeftCentre); - if (CurrentFrame?.MouseLeft2 == true) - actions.Add(TaikoAction.RightCentre); - - return new List { new ReplayState { PressedActions = actions } }; - } + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; } } diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index 0c60cdc109..b2fd62d751 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -1,17 +1,31 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Replays { - public class TaikoReplayFrame : ReplayFrame + public class TaikoReplayFrame : ReplayFrame, IConvertibleReplayFrame { - public override bool IsImportant => MouseLeft || MouseRight; + public List Actions = new List(); - public TaikoReplayFrame(double time, ReplayButtonState buttons) - : base(time, null, null, buttons) + public TaikoReplayFrame(double time, params TaikoAction[] actions) + : base(time) { + Actions.AddRange(actions); + } + + public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + { + if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); + if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); + if (legacyFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre); + if (legacyFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index fd31f738ee..eb282c53ca 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Taiko.Replays; using OpenTK; using System.Linq; using osu.Framework.Input; +using osu.Game.Input.Handlers; using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Taiko.UI @@ -133,6 +134,6 @@ namespace osu.Game.Rulesets.Taiko.UI return null; } - protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); + protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); } } diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index c245407bbf..80fd099f2f 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Game.Input.Handlers; using OpenTK; using OpenTK.Input; @@ -17,14 +16,15 @@ namespace osu.Game.Rulesets.Replays /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. /// It handles logic of any frames which *must* be executed. /// - public abstract class FramedReplayInputHandler : ReplayInputHandler + public abstract class FramedReplayInputHandler : ReplayInputHandler + where TFrame : ReplayFrame { private readonly Replay replay; protected List Frames => replay.Frames; - public ReplayFrame CurrentFrame => !hasFrames ? null : Frames[currentFrameIndex]; - public ReplayFrame NextFrame => !hasFrames ? null : Frames[nextFrameIndex]; + public TFrame CurrentFrame => !HasFrames ? null : (TFrame)Frames[currentFrameIndex]; + public TFrame NextFrame => !HasFrames ? null : (TFrame)Frames[nextFrameIndex]; private int currentFrameIndex; @@ -46,31 +46,14 @@ namespace osu.Game.Rulesets.Replays return true; } - public void SetPosition(Vector2 pos) - { - } - - protected Vector2? Position - { - get - { - if (!hasFrames) - return null; - - return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); - } - } - public override List GetPendingStates() => new List(); public bool AtLastFrame => currentFrameIndex == Frames.Count - 1; public bool AtFirstFrame => currentFrameIndex == 0; - public Vector2 Size => new Vector2(512, 384); - private const double sixty_frame_time = 1000.0 / 60; - private double currentTime; + protected double CurrentTime; private int currentDirection; /// @@ -79,14 +62,16 @@ namespace osu.Game.Rulesets.Replays /// public bool FrameAccuratePlayback = true; - private bool hasFrames => Frames.Count > 0; + protected bool HasFrames => Frames.Count > 0; private bool inImportantSection => - FrameAccuratePlayback && + HasFrames && FrameAccuratePlayback && //a button is in a pressed state - ((currentDirection > 0 ? CurrentFrame : NextFrame)?.IsImportant ?? false) && + IsImportant(currentDirection > 0 ? CurrentFrame : NextFrame) && //the next frame is within an allowable time span - Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; + Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; + + protected virtual bool IsImportant(TFrame frame) => false; /// /// Update the current frame based on an incoming time value. @@ -97,10 +82,10 @@ namespace osu.Game.Rulesets.Replays /// The usable time value. If null, we should not advance time as we do not have enough data. public override double? SetFrameFromTime(double time) { - currentDirection = time.CompareTo(currentTime); + currentDirection = time.CompareTo(CurrentTime); if (currentDirection == 0) currentDirection = 1; - if (hasFrames) + if (HasFrames) { // check if the next frame is in the "future" for the current playback direction if (currentDirection != time.CompareTo(NextFrame.Time)) @@ -114,12 +99,12 @@ namespace osu.Game.Rulesets.Replays // If going backwards, we need to execute once _before_ the frame time to reverse any judgements // that would occur as a result of this frame in forward playback if (currentDirection == -1) - return currentTime = CurrentFrame.Time - 1; - return currentTime = CurrentFrame.Time; + return CurrentTime = CurrentFrame.Time - 1; + return CurrentTime = CurrentFrame.Time; } } - return currentTime = time; + return CurrentTime = time; } protected class ReplayMouseState : MouseState diff --git a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs new file mode 100644 index 0000000000..76709cbf21 --- /dev/null +++ b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Rulesets.Replays.Legacy +{ + public class LegacyReplayFrame : ReplayFrame + { + public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); + + public float? MouseX; + public float? MouseY; + + public bool MouseLeft => MouseLeft1 || MouseLeft2; + public bool MouseRight => MouseRight1 || MouseRight2; + + public bool MouseLeft1 + { + get { return (ButtonState & ReplayButtonState.Left1) > 0; } + set { setButtonState(ReplayButtonState.Left1, value); } + } + public bool MouseRight1 + { + get { return (ButtonState & ReplayButtonState.Right1) > 0; } + set { setButtonState(ReplayButtonState.Right1, value); } + } + public bool MouseLeft2 + { + get { return (ButtonState & ReplayButtonState.Left2) > 0; } + set { setButtonState(ReplayButtonState.Left2, value); } + } + public bool MouseRight2 + { + get { return (ButtonState & ReplayButtonState.Right2) > 0; } + set { setButtonState(ReplayButtonState.Right2, value); } + } + + private void setButtonState(ReplayButtonState singleButton, bool pressed) + { + if (pressed) + ButtonState |= singleButton; + else + ButtonState &= ~singleButton; + } + + public ReplayButtonState ButtonState; + + public LegacyReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) + : base(time) + { + MouseX = mouseX; + MouseY = mouseY; + ButtonState = buttonState; + } + + public override string ToString() + { + return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; + } + } +} diff --git a/osu.Game/Rulesets/Replays/ReplayButtonState.cs b/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs similarity index 85% rename from osu.Game/Rulesets/Replays/ReplayButtonState.cs rename to osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs index 4421a79af8..d0706411d2 100644 --- a/osu.Game/Rulesets/Replays/ReplayButtonState.cs +++ b/osu.Game/Rulesets/Replays/Legacy/ReplayButtonState.cs @@ -3,7 +3,7 @@ using System; -namespace osu.Game.Rulesets.Replays +namespace osu.Game.Rulesets.Replays.Legacy { [Flags] public enum ReplayButtonState diff --git a/osu.Game/Rulesets/Replays/Replay.cs b/osu.Game/Rulesets/Replays/Replay.cs index 27a77addba..a0ea2c5655 100644 --- a/osu.Game/Rulesets/Replays/Replay.cs +++ b/osu.Game/Rulesets/Replays/Replay.cs @@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Replays public class Replay { public User User; - public List Frames = new List(); } } diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs index 4f8ed5163e..61a3646024 100644 --- a/osu.Game/Rulesets/Replays/ReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs @@ -1,70 +1,19 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; - namespace osu.Game.Rulesets.Replays { public class ReplayFrame { - public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); - - public virtual bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight); - - public float? MouseX; - public float? MouseY; - - public bool MouseLeft => MouseLeft1 || MouseLeft2; - public bool MouseRight => MouseRight1 || MouseRight2; - - public bool MouseLeft1 - { - get { return (ButtonState & ReplayButtonState.Left1) > 0; } - set { setButtonState(ReplayButtonState.Left1, value); } - } - public bool MouseRight1 - { - get { return (ButtonState & ReplayButtonState.Right1) > 0; } - set { setButtonState(ReplayButtonState.Right1, value); } - } - public bool MouseLeft2 - { - get { return (ButtonState & ReplayButtonState.Left2) > 0; } - set { setButtonState(ReplayButtonState.Left2, value); } - } - public bool MouseRight2 - { - get { return (ButtonState & ReplayButtonState.Right2) > 0; } - set { setButtonState(ReplayButtonState.Right2, value); } - } - - private void setButtonState(ReplayButtonState singleButton, bool pressed) - { - if (pressed) - ButtonState |= singleButton; - else - ButtonState &= ~singleButton; - } - public double Time; - public ReplayButtonState ButtonState; - - protected ReplayFrame() + public ReplayFrame() { } - public ReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) + public ReplayFrame(double time) { - MouseX = mouseX; - MouseY = mouseY; - ButtonState = buttonState; Time = time; } - - public override string ToString() - { - return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; - } } } diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs new file mode 100644 index 0000000000..16943e5c1d --- /dev/null +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Replays.Types +{ + public interface IConvertibleReplayFrame + { + void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap); + } +} diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 4f256621fb..9d49ebe37d 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -11,6 +11,7 @@ using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -89,6 +90,8 @@ namespace osu.Game.Rulesets /// A descriptive name of the variant. public virtual string GetVariantName(int variant) => string.Empty; + public virtual ReplayFrame CreateReplayFrame() => new ReplayFrame(); + /// /// Create a ruleset info based on this ruleset. /// diff --git a/osu.Game/Rulesets/Scoring/IScoreParser.cs b/osu.Game/Rulesets/Scoring/IScoreParser.cs new file mode 100644 index 0000000000..c7295deb2d --- /dev/null +++ b/osu.Game/Rulesets/Scoring/IScoreParser.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; + +namespace osu.Game.Rulesets.Scoring +{ + public interface IScoreParser + { + Score Parse(Stream stream); + } +} diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs new file mode 100644 index 0000000000..73350919de --- /dev/null +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Game.Beatmaps; +using osu.Game.IO.Legacy; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Legacy; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Users; +using SharpCompress.Compressors.LZMA; + +namespace osu.Game.Rulesets.Scoring.Legacy +{ + public class LegacyScoreParser : IScoreParser + { + private readonly RulesetStore rulesets; + private readonly BeatmapManager beatmaps; + + public LegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps) + { + this.rulesets = rulesets; + this.beatmaps = beatmaps; + } + + private Beatmap currentBeatmap; + private Score currentScore; + private Ruleset currentRuleset; + + public Score Parse(Stream stream) + { + Score score; + + using (SerializationReader sr = new SerializationReader(stream)) + { + currentScore = score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) }; + currentRuleset = score.Ruleset.CreateInstance(); + + /* score.Pass = true;*/ + var version = sr.ReadInt32(); + + /* score.FileChecksum = */ + var beatmapHash = sr.ReadString(); + score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash); + currentBeatmap = beatmaps.GetWorkingBeatmap(score.Beatmap).Beatmap; + + /* score.PlayerName = */ + score.User = new User { Username = sr.ReadString() }; + /* var localScoreChecksum = */ + sr.ReadString(); + /* score.Count300 = */ + sr.ReadUInt16(); + /* score.Count100 = */ + sr.ReadUInt16(); + /* score.Count50 = */ + sr.ReadUInt16(); + /* score.CountGeki = */ + sr.ReadUInt16(); + /* score.CountKatu = */ + sr.ReadUInt16(); + /* score.CountMiss = */ + sr.ReadUInt16(); + score.TotalScore = sr.ReadInt32(); + score.MaxCombo = sr.ReadUInt16(); + /* score.Perfect = */ + sr.ReadBoolean(); + /* score.EnabledMods = (Mods)*/ + sr.ReadInt32(); + /* score.HpGraphString = */ + sr.ReadString(); + /* score.Date = */ + sr.ReadDateTime(); + + var compressedReplay = sr.ReadByteArray(); + + if (version >= 20140721) + /*OnlineId =*/ + sr.ReadInt64(); + else if (version >= 20121008) + /*OnlineId =*/ + sr.ReadInt32(); + + using (var replayInStream = new MemoryStream(compressedReplay)) + { + byte[] properties = new byte[5]; + if (replayInStream.Read(properties, 0, 5) != 5) + throw new IOException("input .lzma is too short"); + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = replayInStream.ReadByte(); + if (v < 0) + throw new IOException("Can't Read 1"); + outSize |= (long)(byte)v << (8 * i); + } + + long compressedSize = replayInStream.Length - replayInStream.Position; + + using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) + using (var reader = new StreamReader(lzma)) + { + score.Replay = new Replay { User = score.User }; + readLegacyReplay(score.Replay, reader); + } + } + } + + return score; + } + + private void readLegacyReplay(Replay replay, StreamReader reader) + { + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4) + continue; + + if (split[0] == "-12345") + { + // Todo: The seed is provided in split[3], which we'll need to use at some point + continue; + } + + var diff = float.Parse(split[0]); + lastTime += diff; + + // Todo: At some point we probably want to rewind and play back the negative-time frames + // but for now we'll achieve equal playback to stable by skipping negative frames + if (diff < 0) + continue; + + replay.Frames.Add(convertFrame(new LegacyReplayFrame(lastTime, float.Parse(split[1]), float.Parse(split[2]), (ReplayButtonState)int.Parse(split[3])))); + } + } + + private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame) + { + var converted = currentRuleset.CreateReplayFrame(); + converted.Time = legacyFrame.Time; + + if (converted is IConvertibleReplayFrame convertible) + convertible.ConvertFrom(legacyFrame, currentScore, currentBeatmap); + + return converted; + } + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index 7abee0b04f..cb2b76cdcf 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -2,16 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.IO; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.IO.Legacy; using osu.Game.IPC; -using osu.Game.Rulesets.Replays; -using osu.Game.Users; -using SharpCompress.Compressors.LZMA; +using osu.Game.Rulesets.Scoring.Legacy; namespace osu.Game.Rulesets.Scoring { @@ -53,127 +49,8 @@ namespace osu.Game.Rulesets.Scoring public Score ReadReplayFile(string replayFilename) { - Score score; - using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) - using (SerializationReader sr = new SerializationReader(s)) - { - score = new Score - { - Ruleset = rulesets.GetRuleset(sr.ReadByte()) - }; - - /* score.Pass = true;*/ - var version = sr.ReadInt32(); - /* score.FileChecksum = */ - var beatmapHash = sr.ReadString(); - score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash); - /* score.PlayerName = */ - score.User = new User { Username = sr.ReadString() }; - /* var localScoreChecksum = */ - sr.ReadString(); - /* score.Count300 = */ - sr.ReadUInt16(); - /* score.Count100 = */ - sr.ReadUInt16(); - /* score.Count50 = */ - sr.ReadUInt16(); - /* score.CountGeki = */ - sr.ReadUInt16(); - /* score.CountKatu = */ - sr.ReadUInt16(); - /* score.CountMiss = */ - sr.ReadUInt16(); - score.TotalScore = sr.ReadInt32(); - score.MaxCombo = sr.ReadUInt16(); - /* score.Perfect = */ - sr.ReadBoolean(); - /* score.EnabledMods = (Mods)*/ - sr.ReadInt32(); - /* score.HpGraphString = */ - sr.ReadString(); - /* score.Date = */ - sr.ReadDateTime(); - - var compressedReplay = sr.ReadByteArray(); - - if (version >= 20140721) - /*OnlineId =*/ - sr.ReadInt64(); - else if (version >= 20121008) - /*OnlineId =*/ - sr.ReadInt32(); - - using (var replayInStream = new MemoryStream(compressedReplay)) - { - byte[] properties = new byte[5]; - if (replayInStream.Read(properties, 0, 5) != 5) - throw new IOException("input .lzma is too short"); - long outSize = 0; - for (int i = 0; i < 8; i++) - { - int v = replayInStream.ReadByte(); - if (v < 0) - throw new IOException("Can't Read 1"); - outSize |= (long)(byte)v << (8 * i); - } - - long compressedSize = replayInStream.Length - replayInStream.Position; - - using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) - using (var reader = new StreamReader(lzma)) - { - score.Replay = createLegacyReplay(reader); - score.Replay.User = score.User; - } - } - } - - return score; + return new LegacyScoreParser(rulesets, beatmaps).Parse(s); } - - /// - /// Creates a legacy replay which is read from a stream. - /// - /// The stream reader. - /// The legacy replay. - private Replay createLegacyReplay(StreamReader reader) - { - var frames = new List(); - - float lastTime = 0; - - foreach (var l in reader.ReadToEnd().Split(',')) - { - var split = l.Split('|'); - - if (split.Length < 4) - continue; - - if (split[0] == "-12345") - { - // Todo: The seed is provided in split[3], which we'll need to use at some point - continue; - } - - var diff = float.Parse(split[0]); - lastTime += diff; - - // Todo: At some point we probably want to rewind and play back the negative-time frames - // but for now we'll achieve equal playback to stable by skipping negative frames - if (diff < 0) - continue; - - frames.Add(new ReplayFrame( - lastTime, - float.Parse(split[1]), - float.Parse(split[2]), - (ReplayButtonState)int.Parse(split[3]) - )); - } - - return new Replay { Frames = frames }; - } - } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 05cb0f741b..780bc5c86b 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -17,6 +17,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Game.Configuration; +using osu.Game.Input.Handlers; using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Replays; @@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.UI /// The input manager. public abstract PassThroughInputManager CreateInputManager(); - protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => null; + protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; public Replay Replay { get; private set; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6a06bf540b..568a176e8a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -361,6 +361,12 @@ + + + + + + @@ -706,8 +712,6 @@ - - @@ -936,4 +940,4 @@ - + \ No newline at end of file From b8b869e8b76ce041fb065071cfe637141353f5d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 00:33:20 +0900 Subject: [PATCH 02/18] Remove now unused property --- osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs index 113f4f2164..cb500735f7 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/StageDefinition.cs @@ -15,11 +15,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// public int Columns; - /// - /// Whether this stage has a special column. - /// - public bool HasSpecialColumn => Columns % 2 == 1; - /// /// Whether the column index is a special column for this stage. /// From a02eaf0e94ad08817eb70e16899918109e499f53 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 00:36:32 +0900 Subject: [PATCH 03/18] Use StageDefinition to determine special column in ManiaReplayFrame --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index ed1143d8ff..6c38c5e91a 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Replays // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. - bool isSpecialColumn(int column) => converter.TargetColumns % 2 == 1 && column == converter.TargetColumns / 2; + var stage = new StageDefinition { Columns = converter.TargetColumns }; var normalAction = ManiaAction.Key1; var specialAction = ManiaAction.Special1; @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Replays { Actions.Add((activeColumns & 1) > 0 ? specialAction : normalAction); - if (isSpecialColumn(counter)) + if (stage.IsSpecialColumn(counter)) normalAction++; else specialAction++; From 222ae700624c7ced4daf7cde9a9a62f8b1861e2f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 00:57:35 +0900 Subject: [PATCH 04/18] Populate rulesets with their replay frames --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++++ osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs | 4 ++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++++ osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 4 ++++ osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs | 1 - osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs | 4 ++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++++ 7 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 5e70239c7c..0228a23166 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Catch.Replays; +using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Catch { @@ -101,6 +103,8 @@ namespace osu.Game.Rulesets.Catch public override int LegacyID => 2; + public override ReplayFrame CreateReplayFrame() => new CatchReplayFrame(); + public CatchRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) { diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index c81b095f30..3909522b52 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -14,6 +14,10 @@ namespace osu.Game.Rulesets.Catch.Replays public float X; public bool Dashing; + public CatchReplayFrame() + { + } + public CatchReplayFrame(double time, float? x = null, bool dashing = false) : base(time) { diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 3bfb4d3e44..611c5e7143 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -12,6 +12,8 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Mania { @@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Mania public override int LegacyID => 3; + public override ReplayFrame CreateReplayFrame() => new ManiaReplayFrame(); + public ManiaRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) { diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 6c38c5e91a..d9c998be67 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -15,6 +15,10 @@ namespace osu.Game.Rulesets.Mania.Replays { public List Actions = new List(); + public ManiaReplayFrame() + { + } + public ManiaReplayFrame(double time, params ManiaAction[] actions) : base(time) { diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 5d0ff25109..4e0d22abd1 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Osu.Replays public List Actions = new List(); public OsuReplayFrame() - : base(0) { } diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index b2fd62d751..335e5e1b6f 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -14,6 +14,10 @@ namespace osu.Game.Rulesets.Taiko.Replays { public List Actions = new List(); + public TaikoReplayFrame() + { + } + public TaikoReplayFrame(double time, params TaikoAction[] actions) : base(time) { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 50cc80db50..9965b3d3f2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Taiko.Replays; namespace osu.Game.Rulesets.Taiko { @@ -103,6 +105,8 @@ namespace osu.Game.Rulesets.Taiko public override int LegacyID => 1; + public override ReplayFrame CreateReplayFrame() => new TaikoReplayFrame(); + public TaikoRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) { From 91460f27daf5adabeb1fe40347f6121263998001 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 00:57:45 +0900 Subject: [PATCH 05/18] Fix incorrect isForCurrentRuleset value --- osu.Game.Tests/Visual/TestCaseReplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseReplay.cs b/osu.Game.Tests/Visual/TestCaseReplay.cs index 237687458d..115ac11919 100644 --- a/osu.Game.Tests/Visual/TestCaseReplay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplay.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here // to simulate setting a replay rather than having the replay already set for us beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); - var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, false); + var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo)); // We have the replay var replay = dummyRulesetContainer.Replay; From 4a20513fc49aa0d234966145f0605155bae39eaf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:02:12 +0900 Subject: [PATCH 06/18] Fix catch always dashing --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index d0772bbaa1..f1503a14ee 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Replays } else if (h.HyperDash) { - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition, true)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } else if (dashRequired) @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.Replays { double timeBefore = positionChange / movement_speed; - Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition, true)); + Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition)); Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); } From a7e6c19e61d15a22d665b3405bd62b882988d98f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:18:18 +0900 Subject: [PATCH 07/18] Remove todo (probably not a consideration going forward) --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index d9c998be67..a9d0e96110 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Mania.Replays // We don't need to fully convert, just create the converter var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.Ruleset.Equals(score.Ruleset), beatmap); - // Todo: Apply mods to converter // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. From ab4d7fa46ec0cc0bd62d1cc5057e6536401b38f4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:24:36 +0900 Subject: [PATCH 08/18] Remove IScoreParser interface It's too early for this. --- osu.Game/Rulesets/Scoring/IScoreParser.cs | 12 ------------ .../Rulesets/Scoring/Legacy/LegacyScoreParser.cs | 2 +- osu.Game/osu.Game.csproj | 1 - 3 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 osu.Game/Rulesets/Scoring/IScoreParser.cs diff --git a/osu.Game/Rulesets/Scoring/IScoreParser.cs b/osu.Game/Rulesets/Scoring/IScoreParser.cs deleted file mode 100644 index c7295deb2d..0000000000 --- a/osu.Game/Rulesets/Scoring/IScoreParser.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; - -namespace osu.Game.Rulesets.Scoring -{ - public interface IScoreParser - { - Score Parse(Stream stream); - } -} diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 73350919de..5610a36a83 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -12,7 +12,7 @@ using SharpCompress.Compressors.LZMA; namespace osu.Game.Rulesets.Scoring.Legacy { - public class LegacyScoreParser : IScoreParser + public class LegacyScoreParser { private readonly RulesetStore rulesets; private readonly BeatmapManager beatmaps; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 568a176e8a..6d3746e30a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -365,7 +365,6 @@ - From c9c65cab53548490585ee0c650ed7956345aa1de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:32:32 +0900 Subject: [PATCH 09/18] CreateReplayFrame -> CreateConvertibleReplayFrame --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++-- osu.Game/Rulesets/Ruleset.cs | 4 ++-- .../Rulesets/Scoring/Legacy/LegacyScoreParser.cs | 13 ++++++------- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 0228a23166..d49e9c7c26 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -11,7 +11,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Catch.Replays; -using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; namespace osu.Game.Rulesets.Catch { @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Catch public override int LegacyID => 2; - public override ReplayFrame CreateReplayFrame() => new CatchReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); public CatchRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 611c5e7143..3ad498e6ea 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; namespace osu.Game.Rulesets.Mania { @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania public override int LegacyID => 3; - public override ReplayFrame CreateReplayFrame() => new ManiaReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); public ManiaRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 42ead91f0d..3f0aea5cb2 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -20,7 +20,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; namespace osu.Game.Rulesets.Osu { @@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu public override int LegacyID => 0; - public override ReplayFrame CreateReplayFrame() => new OsuReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); public OsuRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 9965b3d3f2..713506e831 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.UI; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; -using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Taiko.Replays; namespace osu.Game.Rulesets.Taiko @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Taiko public override int LegacyID => 1; - public override ReplayFrame CreateReplayFrame() => new TaikoReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); public TaikoRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 9d49ebe37d..7b6219da9a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets /// A descriptive name of the variant. public virtual string GetVariantName(int variant) => string.Empty; - public virtual ReplayFrame CreateReplayFrame() => new ReplayFrame(); + public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null; /// /// Create a ruleset info based on this ruleset. diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 5610a36a83..ea36b818b3 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -1,12 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.IO; using osu.Game.Beatmaps; using osu.Game.IO.Legacy; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Users; using SharpCompress.Compressors.LZMA; @@ -139,13 +139,12 @@ namespace osu.Game.Rulesets.Scoring.Legacy private ReplayFrame convertFrame(LegacyReplayFrame legacyFrame) { - var converted = currentRuleset.CreateReplayFrame(); - converted.Time = legacyFrame.Time; + var convertible = currentRuleset.CreateConvertibleReplayFrame(); + if (convertible == null) + throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); + convertible.ConvertFrom(legacyFrame, currentScore, currentBeatmap); - if (converted is IConvertibleReplayFrame convertible) - convertible.ConvertFrom(legacyFrame, currentScore, currentBeatmap); - - return converted; + return (ReplayFrame)convertible; } } } From cf42d5bbd50f0df1cddb05826d234ead27921b89 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:38:40 +0900 Subject: [PATCH 10/18] Remove mostly-unused Score parameter --- osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs | 3 +-- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 5 ++--- osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs | 3 +-- osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs | 3 +-- .../Replays/Types/IConvertibleReplayFrame.cs | 12 ++++++++++-- .../Rulesets/Scoring/Legacy/LegacyScoreParser.cs | 5 ++--- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index 3909522b52..d45223a1cb 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -5,7 +5,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Replays { @@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Replays Dashing = dashing; } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { // Todo: This needs to be re-scaled X = legacyFrame.Position.X; diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index a9d0e96110..9e773d0e24 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -7,7 +7,6 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Replays { @@ -25,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { // We don't need to fully convert, just create the converter - var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.Ruleset.Equals(score.Ruleset), beatmap); + var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap); // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs index 4e0d22abd1..bcdfe07417 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs @@ -6,7 +6,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; using OpenTK; namespace osu.Game.Rulesets.Osu.Replays @@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { Position = legacyFrame.Position; if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs index 335e5e1b6f..6cd63f6c70 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs @@ -6,7 +6,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.Replays { @@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Replays Actions.AddRange(actions); } - public void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap) + public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs index 16943e5c1d..ac1e5e29ec 100644 --- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs @@ -3,12 +3,20 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Replays.Legacy; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Replays.Types { + /// + /// A type of which can be converted from a . + /// public interface IConvertibleReplayFrame { - void ConvertFrom(LegacyReplayFrame legacyFrame, Score score, Beatmap beatmap); + /// + /// Populates this using values from a . + /// + /// The to extract values from. + /// The score. + /// The beatmap. + void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); } } diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index ea36b818b3..998d2aa0b7 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Scoring.Legacy } private Beatmap currentBeatmap; - private Score currentScore; private Ruleset currentRuleset; public Score Parse(Stream stream) @@ -33,7 +32,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy using (SerializationReader sr = new SerializationReader(stream)) { - currentScore = score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) }; + score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) }; currentRuleset = score.Ruleset.CreateInstance(); /* score.Pass = true;*/ @@ -142,7 +141,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy var convertible = currentRuleset.CreateConvertibleReplayFrame(); if (convertible == null) throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); - convertible.ConvertFrom(legacyFrame, currentScore, currentBeatmap); + convertible.ConvertFrom(legacyFrame, currentBeatmap); return (ReplayFrame)convertible; } From 19911193cf35bbb89e00297203c310f95595352b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:39:33 +0900 Subject: [PATCH 11/18] Minify LegacyReplayFrame --- .../Replays/Legacy/LegacyReplayFrame.cs | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs index 76709cbf21..945cb95e79 100644 --- a/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/Legacy/LegacyReplayFrame.cs @@ -15,34 +15,10 @@ namespace osu.Game.Rulesets.Replays.Legacy public bool MouseLeft => MouseLeft1 || MouseLeft2; public bool MouseRight => MouseRight1 || MouseRight2; - public bool MouseLeft1 - { - get { return (ButtonState & ReplayButtonState.Left1) > 0; } - set { setButtonState(ReplayButtonState.Left1, value); } - } - public bool MouseRight1 - { - get { return (ButtonState & ReplayButtonState.Right1) > 0; } - set { setButtonState(ReplayButtonState.Right1, value); } - } - public bool MouseLeft2 - { - get { return (ButtonState & ReplayButtonState.Left2) > 0; } - set { setButtonState(ReplayButtonState.Left2, value); } - } - public bool MouseRight2 - { - get { return (ButtonState & ReplayButtonState.Right2) > 0; } - set { setButtonState(ReplayButtonState.Right2, value); } - } - - private void setButtonState(ReplayButtonState singleButton, bool pressed) - { - if (pressed) - ButtonState |= singleButton; - else - ButtonState &= ~singleButton; - } + public bool MouseLeft1 => (ButtonState & ReplayButtonState.Left1) > 0; + public bool MouseRight1 => (ButtonState & ReplayButtonState.Right1) > 0; + public bool MouseLeft2 => (ButtonState & ReplayButtonState.Left2) > 0; + public bool MouseRight2 => (ButtonState & ReplayButtonState.Right2) > 0; public ReplayButtonState ButtonState; From fb16c3db877ad71cfaf6f81243e58aac27d42005 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:40:35 +0900 Subject: [PATCH 12/18] Privatise CurrentTime's setter --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 80fd099f2f..5ffd67423e 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Replays private const double sixty_frame_time = 1000.0 / 60; - protected double CurrentTime; + protected double CurrentTime { get; private set; } private int currentDirection; /// From 195b6642e62438ad3c08b02d8df5b4e69b84dc14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:45:41 +0900 Subject: [PATCH 13/18] Reimplement important frames --- .../Replays/CatchFramedReplayInputHandler.cs | 2 ++ .../Replays/ManiaFramedReplayInputHandler.cs | 3 +++ osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs | 3 +++ .../Replays/TaikoFramedReplayInputHandler.cs | 3 +++ 4 files changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 795a859f5f..9c0d05d4cd 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.Replays { } + protected override bool IsImportant(CatchReplayFrame frame) => frame.X > 0; + protected float? Position { get diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs index fb9ae37831..3541561418 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Input; using osu.Game.Rulesets.Replays; @@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Mania.Replays { } + protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any(); + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; } } diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs index aef02dad7e..0a61b0f199 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Input; using osu.Framework.MathUtils; using osu.Game.Rulesets.Replays; @@ -16,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Replays { } + protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any(); + protected Vector2? Position { get diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index 1c525dcda0..c80bddc304 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -3,6 +3,7 @@ using osu.Game.Rulesets.Replays; using System.Collections.Generic; +using System.Linq; using osu.Framework.Input; namespace osu.Game.Rulesets.Taiko.Replays @@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Replays { } + protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any(); + public override List GetPendingStates() => new List { new ReplayState { PressedActions = CurrentFrame.Actions } }; } } From a24e8b02e806618a0209e0611890567cd71f3620 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 01:48:13 +0900 Subject: [PATCH 14/18] Rename catch frame's X to Position --- .../Replays/CatchFramedReplayInputHandler.cs | 8 ++++---- osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 9c0d05d4cd..9c9b06fcea 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Replays { } - protected override bool IsImportant(CatchReplayFrame frame) => frame.X > 0; + protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0; protected float? Position { @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Replays if (!HasFrames) return null; - return Interpolation.ValueAt(CurrentTime, CurrentFrame.X, NextFrame.X, CurrentFrame.Time, NextFrame.Time); + return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); } } @@ -37,9 +37,9 @@ namespace osu.Game.Rulesets.Catch.Replays if (CurrentFrame.Dashing) actions.Add(CatchAction.Dash); - if (Position.Value > CurrentFrame.X) + if (Position.Value > CurrentFrame.Position) actions.Add(CatchAction.MoveRight); - else if (Position.Value < CurrentFrame.X) + else if (Position.Value < CurrentFrame.Position) actions.Add(CatchAction.MoveLeft); return new List diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index d45223a1cb..3b2d0d58b3 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -10,24 +10,24 @@ namespace osu.Game.Rulesets.Catch.Replays { public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame { - public float X; + public float Position; public bool Dashing; public CatchReplayFrame() { } - public CatchReplayFrame(double time, float? x = null, bool dashing = false) + public CatchReplayFrame(double time, float? position = null, bool dashing = false) : base(time) { - X = x ?? -1; + Position = position ?? -1; Dashing = dashing; } public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { // Todo: This needs to be re-scaled - X = legacyFrame.Position.X; + Position = legacyFrame.Position.X; Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; } } From e869f7d05be3bd0eb1e2de5b03e67c9c96ceca2e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 02:09:23 +0900 Subject: [PATCH 15/18] Fix converted frames not getting time values --- osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs index 998d2aa0b7..9ebb62a368 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs @@ -143,7 +143,10 @@ namespace osu.Game.Rulesets.Scoring.Legacy throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); convertible.ConvertFrom(legacyFrame, currentBeatmap); - return (ReplayFrame)convertible; + var frame = (ReplayFrame)convertible; + frame.Time = legacyFrame.Time; + + return frame; } } } From c783a19e41b431763eec7e725be5f97df78f0e32 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 02:09:35 +0900 Subject: [PATCH 16/18] Fix mania frame conversion not working at all --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 9e773d0e24..9990f89b99 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -41,12 +41,15 @@ namespace osu.Game.Rulesets.Mania.Replays int counter = 0; while (activeColumns > 0) { - Actions.Add((activeColumns & 1) > 0 ? specialAction : normalAction); + var isSpecial = stage.IsSpecialColumn(counter); - if (stage.IsSpecialColumn(counter)) - normalAction++; - else + if ((activeColumns & 1) > 0) + Actions.Add(isSpecial ? specialAction : normalAction); + + if (isSpecial) specialAction++; + else + normalAction++; counter++; activeColumns >>= 1; From 7d7a3bab0e86251710f0825c602719805f742f9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 14:43:45 +0900 Subject: [PATCH 17/18] Fix catch legacy replay positions not being relative to playfield size --- osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs index 3b2d0d58b3..b444b0d7ba 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Types; @@ -26,8 +27,7 @@ namespace osu.Game.Rulesets.Catch.Replays public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) { - // Todo: This needs to be re-scaled - Position = legacyFrame.Position.X; + Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; } } From 8273288048b6e45375d933f5b244a7eae9397b45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 4 Mar 2018 02:12:45 +0900 Subject: [PATCH 18/18] Add xmldoc --- osu.Game/Rulesets/Ruleset.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 7b6219da9a..f9b64995f9 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -90,6 +90,11 @@ namespace osu.Game.Rulesets /// A descriptive name of the variant. public virtual string GetVariantName(int variant) => string.Empty; + /// + /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame + /// for conversion use. + /// + /// An empty frame for the current ruleset, or null if unsupported. public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null; ///