From b28689c774c8a0e4bc029eda00cb5a4083ad040e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Oct 2019 15:00:47 +0800 Subject: [PATCH 1/3] Fix key counters appearing negative on intense beatmaps When `FrameStabilityContainer` decides it needs multiple updates on the same frame, it ends up with an elapsed time of zero. This was interacting badly with the condition used in `RulesetInputManager` to govern playback direction. I have changed this to use `Rate` as exposed by the frame stable clock. - Closes #6198. --- .../Rulesets/UI/FrameStabilityContainer.cs | 28 ++++++++++--------- osu.Game/Rulesets/UI/RulesetInputManager.cs | 4 +-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 05d3c02381..74d3439c59 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -47,6 +47,11 @@ namespace osu.Game.Rulesets.UI private IFrameBasedClock parentGameplayClock; + /// + /// The current direction of playback to be exposed to frame stable children. + /// + private int direction; + [BackgroundDependencyLoader(true)] private void load(GameplayClock clock) { @@ -111,16 +116,12 @@ namespace osu.Game.Rulesets.UI validState = true; - manualClock.Rate = parentGameplayClock.Rate; - manualClock.IsRunning = parentGameplayClock.IsRunning; - var newProposedTime = parentGameplayClock.CurrentTime; try { if (!FrameStablePlayback) { - manualClock.CurrentTime = newProposedTime; requireMoreUpdateLoops = false; return; } @@ -128,9 +129,9 @@ namespace osu.Game.Rulesets.UI { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. - manualClock.CurrentTime = newProposedTime; - // do a second process to clear out ElapsedTime + // process frame (in addition to finally clause) to clear out ElapsedTime + manualClock.CurrentTime = newProposedTime; framedClock.ProcessFrame(); firstConsumption = false; @@ -144,11 +145,7 @@ namespace osu.Game.Rulesets.UI : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); } - if (!isAttached) - { - manualClock.CurrentTime = newProposedTime; - } - else + if (isAttached) { double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime); @@ -156,9 +153,7 @@ namespace osu.Game.Rulesets.UI { // we shouldn't execute for this time value. probably waiting on more replay data. validState = false; - requireMoreUpdateLoops = true; - manualClock.CurrentTime = newProposedTime; return; } @@ -169,6 +164,13 @@ namespace osu.Game.Rulesets.UI } finally { + if (newProposedTime != manualClock.CurrentTime) + direction = newProposedTime > manualClock.CurrentTime ? 1 : -1; + + manualClock.CurrentTime = newProposedTime; + manualClock.Rate = Math.Abs(parentGameplayClock.Rate) * direction; + manualClock.IsRunning = parentGameplayClock.IsRunning; + // The manual clock time has changed in the above code. The framed clock now needs to be updated // to ensure that the its time is valid for our children before input is processed framedClock.ProcessFrame(); diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 98e27240d3..5cc213be41 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.UI { } - public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.ElapsedFrameTime > 0)); + public bool OnPressed(T action) => Target.Children.OfType>().Any(c => c.OnPressed(action, Clock.Rate >= 0)); - public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.ElapsedFrameTime > 0)); + public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action, Clock.Rate >= 0)); } #endregion From ddef7fa3ba67d47ff90d62446d7ed5bbf294c46d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 13:32:47 +0800 Subject: [PATCH 2/3] Repair behavioural change --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 74d3439c59..c6d812aee3 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.UI return; } - manualClock.CurrentTime = newTime.Value; + newProposedTime = newTime.Value; } requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; From aeb62825cdb0c0727fca76b6ae21541133aeb1e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 4 Oct 2019 13:42:06 +0800 Subject: [PATCH 3/3] Move out requireMoreUpdateLoops for better consistency --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index c6d812aee3..e569bb8459 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -115,17 +115,16 @@ namespace osu.Game.Rulesets.UI setClock(); // LoadComplete may not be run yet, but we still want the clock. validState = true; + requireMoreUpdateLoops = false; var newProposedTime = parentGameplayClock.CurrentTime; try { if (!FrameStablePlayback) - { - requireMoreUpdateLoops = false; return; - } - else if (firstConsumption) + + if (firstConsumption) { // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour. // Instead we perform an initial seek to the proposed time. @@ -159,8 +158,6 @@ namespace osu.Game.Rulesets.UI newProposedTime = newTime.Value; } - - requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime; } finally { @@ -171,6 +168,8 @@ namespace osu.Game.Rulesets.UI manualClock.Rate = Math.Abs(parentGameplayClock.Rate) * direction; manualClock.IsRunning = parentGameplayClock.IsRunning; + requireMoreUpdateLoops |= manualClock.CurrentTime != parentGameplayClock.CurrentTime; + // The manual clock time has changed in the above code. The framed clock now needs to be updated // to ensure that the its time is valid for our children before input is processed framedClock.ProcessFrame();