From 27fdda8b91c070838c9a8d7f0a4d0092b53f59df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 12:21:49 +0900 Subject: [PATCH 1/8] Don't update hitobject results when rewinding --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ec7e6dc303..fe9f6f9e51 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -243,6 +243,10 @@ internal void OnLifetimeEnd() /// Whether a scoring result has occurred from this or any nested . protected bool UpdateResult(bool userTriggered) { + // It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed + if (Time.Elapsed < 0) + return false; + judgementOccurred = false; if (AllJudged) From 44d2514f1a94b5aa49a36e9966d0d65bf8af9b72 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 14:41:10 +0900 Subject: [PATCH 2/8] Add test scene --- .../Gameplay/TestSceneGameplayRewinding.cs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs new file mode 100644 index 0000000000..b3c98c9aaa --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneGameplayRewinding : PlayerTestScene + { + private RulesetExposingPlayer player => (RulesetExposingPlayer)Player; + + [Resolved] + private AudioManager audioManager { get; set; } + + public TestSceneGameplayRewinding() + : base(new OsuRuleset()) + { + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) + => new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + + [Test] + public void TestNotJudgementsOnRewind() + { + addSeekStep(3000); + AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); + AddStep("clear results", () => player.AppliedResults.Clear()); + addSeekStep(0); + AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); + AddAssert("no results triggered", () => player.AppliedResults.Count == 0); + } + + private void addSeekStep(double time) + { + AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); + + // Allow 2 frames of lenience + AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 32)); + } + + protected override Player CreatePlayer(Ruleset ruleset) + { + Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); + return new RulesetExposingPlayer(); + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } }, + }; + + for (int i = 0; i < 15; i++) + { + beatmap.HitObjects.Add(new HitCircle + { + Position = new Vector2(256, 192), + StartTime = 1000 + 30 * i + }); + } + + return beatmap; + } + + private class RulesetExposingPlayer : Player + { + public readonly List AppliedResults = new List(); + + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; + + public RulesetExposingPlayer() + : base(false, false) + { + } + + [BackgroundDependencyLoader] + private void load() + { + ScoreProcessor.NewJudgement += r => AppliedResults.Add(r); + } + } + } +} From f12caaf9079126123b3eeb44f7d96de49c471a33 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Jun 2019 15:47:21 +0900 Subject: [PATCH 3/8] Increase leniency --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index b3c98c9aaa..0176301c03 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -49,8 +49,8 @@ private void addSeekStep(double time) { AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); - // Allow 2 frames of lenience - AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 32)); + // Allow a few frames of lenience + AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); } protected override Player CreatePlayer(Ruleset ruleset) From 15b9b53d35b0c7c7e4a46b5efc5f89022bf224f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 Jun 2019 13:40:32 +0900 Subject: [PATCH 4/8] Fix IconButtons not being scaled correctly --- .../UserInterface/TestSceneIconButton.cs | 13 +++---------- .../UserInterface/TestSceneMusicController.cs | 3 +-- osu.Game/Graphics/UserInterface/IconButton.cs | 19 ++----------------- osu.Game/Overlays/MusicController.cs | 14 ++++++++++++++ .../Compose/Components/BeatDivisorControl.cs | 2 +- .../Components/Timeline/TimelineButton.cs | 4 ++-- 6 files changed, 23 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs index 0c9ce50288..c80b3e6297 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneIconButton.cs @@ -26,8 +26,7 @@ public TestSceneIconButton() { new NamedIconButton("No change", new IconButton()), new NamedIconButton("Background colours", new ColouredIconButton()), - new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }), - new NamedIconButton("Unchanging size", new IconButton(), false), + new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }), new NamedIconButton("Icon colours", new IconButton { IconColour = Color4.Green, @@ -48,7 +47,7 @@ public ColouredIconButton() private class NamedIconButton : Container { - public NamedIconButton(string name, IconButton button, bool allowSizeChange = true) + public NamedIconButton(string name, IconButton button) { AutoSizeAxes = Axes.Y; Width = 200; @@ -101,13 +100,7 @@ public NamedIconButton(string name, IconButton button, bool allowSizeChange = tr } }; - if (allowSizeChange) - iconContainer.AutoSizeAxes = Axes.Both; - else - { - iconContainer.RelativeSizeAxes = Axes.X; - iconContainer.Height = 30; - } + iconContainer.AutoSizeAxes = Axes.Both; button.Anchor = Anchor.Centre; button.Origin = Anchor.Centre; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs index 2f2a40925f..ab2ca47100 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMusicController.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Timing; using osu.Game.Overlays; @@ -23,9 +22,9 @@ public TestSceneMusicController() }; Add(mc); - AddToggleStep(@"toggle visibility", state => mc.State.Value = state ? Visibility.Visible : Visibility.Hidden); AddStep(@"show", () => mc.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); + AddStep(@"show", () => mc.Hide()); } } } diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index 052e9194fa..27427581fd 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -11,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface { public class IconButton : OsuAnimatedButton { - public const float BUTTON_SIZE = 30; + public const float DEFAULT_BUTTON_SIZE = 30; private Color4? iconColour; @@ -57,26 +57,11 @@ public Vector2 IconScale set => icon.Scale = value; } - /// - /// The size of the while it is not being pressed. - /// - public Vector2 ButtonSize - { - get => Content.Size; - set - { - Content.RelativeSizeAxes = Axes.None; - Content.AutoSizeAxes = Axes.None; - Content.Size = value; - } - } - private readonly SpriteIcon icon; public IconButton() { - AutoSizeAxes = Axes.Both; - ButtonSize = new Vector2(BUTTON_SIZE); + Size = new Vector2(DEFAULT_BUTTON_SIZE); Add(icon = new SpriteIcon { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 85524e992c..8b9bac877b 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -464,12 +464,26 @@ private enum TransformDirection private class MusicIconButton : IconButton { + public MusicIconButton() + { + AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { HoverColour = colours.YellowDark.Opacity(0.6f); FlashColour = colours.Yellow; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // works with AutoSizeAxes above to make buttons autosize with the scale animation. + Content.AutoSizeAxes = Axes.None; + Content.Size = new Vector2(DEFAULT_BUTTON_SIZE); + } } private class Background : BufferedContainer diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index ebf8c9c309..c615656d60 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -171,7 +171,7 @@ public DivisorButton() // Small offset to look a bit better centered along with the divisor text Y = 1; - ButtonSize = new Vector2(20); + Size = new Vector2(20); IconScale = new Vector2(0.6f); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs index 49e97e698b..8865bf31ea 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs @@ -31,14 +31,14 @@ public TimelineButton() InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() }; button.Enabled.BindTo(Enabled); - Width = button.ButtonSize.X; + Width = button.Width; } protected override void Update() { base.Update(); - button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight); + button.Size = new Vector2(button.Width, DrawHeight); } private class TimelineIconButton : IconButton From 4972f862e64ca302c7294e698822dd2727b01d7d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:35:31 +0900 Subject: [PATCH 5/8] Seek track instead --- .../Visual/Gameplay/TestSceneGameplayRewinding.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 0176301c03..787b8ddfed 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Audio.Track; using osu.Framework.MathUtils; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -31,8 +32,14 @@ public TestSceneGameplayRewinding() { } + private Track track; + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) - => new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + { + var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager); + track = working.Track; + return working; + } [Test] public void TestNotJudgementsOnRewind() @@ -47,7 +54,7 @@ public void TestNotJudgementsOnRewind() private void addSeekStep(double time) { - AddStep($"seek to {time}", () => player.GameplayClockContainer.Seek(time)); + AddStep($"seek to {time}", () => track.Seek(time)); // Allow a few frames of lenience AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); From 95082d2fccb0f709ac4f3978f15a1cf8796f4daa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:35:47 +0900 Subject: [PATCH 6/8] Rename testcase --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index 787b8ddfed..a1746084a3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -42,7 +42,7 @@ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) } [Test] - public void TestNotJudgementsOnRewind() + public void TestNoJudgementsOnRewind() { addSeekStep(3000); AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); From 0c293be89cccbd1d12f0e22cf4997aee49520cf3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 19:51:31 +0900 Subject: [PATCH 7/8] Wait for track to start running before seeking --- osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index a1746084a3..237fee1594 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -44,6 +44,7 @@ protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) [Test] public void TestNoJudgementsOnRewind() { + AddUntilStep("wait for track to start running", () => track.IsRunning); addSeekStep(3000); AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); AddStep("clear results", () => player.AppliedResults.Clear()); From 512b9dfd820d0e6a1c6852979f1a825b6d2e8fb5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Jun 2019 20:18:22 +0900 Subject: [PATCH 8/8] Fix potential songselect testcase failures --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index f5115c50a9..738b7f14f3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -18,6 +18,7 @@ using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Select; @@ -100,8 +101,11 @@ private void load(GameHost host, AudioManager audio) } [SetUp] - public virtual void SetUp() => - Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); }); + public virtual void SetUp() => Schedule(() => + { + Ruleset.Value = new OsuRuleset().RulesetInfo; + manager?.Delete(manager.GetAllUsableBeatmapSets()); + }); [Test] public void TestDummy() @@ -185,7 +189,7 @@ public void TestRulesetChangeResetsMods() AddAssert("empty mods", () => !Mods.Value.Any()); void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; - void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex--; + void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex++; } [Test]