diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 666c3e41a4..fa564cdd61 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play public BeatmapInfo BeatmapInfo; - public bool IsPaused { get; private set; } + public bool IsPaused => !interpolatedSourceClock.IsRunning; public bool HasFailed { get; private set; } @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play private const double pause_cooldown = 1000; private double lastPauseActionTime; - private bool canPause => Time.Current >= lastPauseActionTime + pause_cooldown; + private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown; private IAdjustableClock sourceClock; private IFrameBasedClock interpolatedSourceClock; @@ -205,20 +205,31 @@ namespace osu.Game.Screens.Play public void Pause(bool force = false) { - if (canPause || force) + if (!canPause && !force) return; + + // the actual pausing is potentially happening on a different thread. + // we want to wait for the source clock to stop so we can be sure all components are in a stable state. + if (!IsPaused) { + sourceClock.Stop(); + + Schedule(() => Pause(force)); + return; + } + + // we need to do a final check after all of our children have processed up to the paused clock time. + // this is to cover cases where, for instance, the player fails in the last processed frame (which would change canPause). + // as the scheduler runs before children updates, let's schedule for the next frame. + Schedule(() => + { + if (!canPause) return; + lastPauseActionTime = Time.Current; hudOverlay.KeyCounter.IsCounting = false; hudOverlay.Progress.Show(); pauseOverlay.Retries = RestartCount; pauseOverlay.Show(); - sourceClock.Stop(); - IsPaused = true; - } - else - { - IsPaused = false; - } + }); } public void Resume() @@ -228,13 +239,6 @@ namespace osu.Game.Screens.Play hudOverlay.Progress.Hide(); pauseOverlay.Hide(); sourceClock.Start(); - IsPaused = false; - } - - public void TogglePaused() - { - IsPaused = !IsPaused; - if (IsPaused) Pause(); else Resume(); } public void Restart() @@ -243,11 +247,11 @@ namespace osu.Game.Screens.Play var newPlayer = new Player(); + ValidForResume = false; + LoadComponentAsync(newPlayer, delegate { newPlayer.RestartCount = RestartCount + 1; - ValidForResume = false; - if (!Push(newPlayer)) { // Error(?) @@ -263,10 +267,11 @@ namespace osu.Game.Screens.Play if (scoreProcessor.HasFailed || onCompletionEvent != null) return; + ValidForResume = false; + Delay(1000); onCompletionEvent = Schedule(delegate { - ValidForResume = false; Push(new Results { Score = scoreProcessor.CreateScore() @@ -278,8 +283,6 @@ namespace osu.Game.Screens.Play { sourceClock.Stop(); - Delay(500); - HasFailed = true; failOverlay.Retries = RestartCount; failOverlay.Show(); @@ -324,12 +327,15 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { + if (HasFailed || !ValidForResume) + return false; + if (pauseOverlay != null && !HitRenderer.HasReplayLoaded) { //pause screen override logic. if (pauseOverlay?.State == Visibility.Hidden && !canPause) return true; - if (!IsPaused && sourceClock.IsRunning) // For if the user presses escape quickly when entering the map + if (!IsPaused) // For if the user presses escape quickly when entering the map { Pause(); return true;