Skip to content

Commit

Permalink
Merge pull request ppy#30623 from peppy/fix-flash-transition-player
Browse files Browse the repository at this point in the history
Fix occasional flash when quick exiting / retrying from player
  • Loading branch information
bdach authored Nov 15, 2024
2 parents b3b1868 + 1a31e56 commit 21d4076
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private Storyboard createStoryboard(double duration)

protected partial class OutroPlayer : TestPlayer
{
public void ExitViaPause() => PerformExit(true);
public void ExitViaPause() => PerformExitWithConfirmation();

public new FailOverlay FailOverlay => base.FailOverlay;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private void failAndBail(string message = null)
if (!string.IsNullOrEmpty(message))
Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important);

Schedule(() => PerformExit(false));
Schedule(() => PerformExit());
}

private void onGameplayStarted() => Scheduler.Add(() =>
Expand Down
63 changes: 40 additions & 23 deletions osu.Game/Screens/Play/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public abstract partial class Player : ScreenWithBeatmapBackground, ISamplePlayb
public Action<bool> RestartRequested;

private bool isRestarting;
private bool skipExitTransition;

private Bindable<bool> mouseWheelDisabled;

Expand Down Expand Up @@ -289,18 +290,15 @@ private void load(OsuConfigManager config, OsuGameBase game, CancellationToken c
{
SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false),
OnRetry = Configuration.AllowUserInteraction ? () => Restart() : null,
OnQuit = () => PerformExit(true),
OnQuit = () => PerformExitWithConfirmation(),
},
new HotkeyExitOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;
if (PerformExit(false))
// The hotkey overlay dims the screen.
// If the operation succeeds, we want to make sure we stay dimmed to keep continuity.
fadeOut(true);
PerformExit(skipTransition: true);
},
},
});
Expand All @@ -318,10 +316,7 @@ private void load(OsuConfigManager config, OsuGameBase game, CancellationToken c
{
if (!this.IsCurrentScreen()) return;
if (Restart(true))
// The hotkey overlay dims the screen.
// If the operation succeeds, we want to make sure we stay dimmed to keep continuity.
fadeOut(true);
Restart(true);
},
},
});
Expand Down Expand Up @@ -448,7 +443,7 @@ private Drawable createOverlayComponents(IWorkingBeatmap working)
{
HoldToQuit =
{
Action = () => PerformExit(true),
Action = () => PerformExitWithConfirmation(),
IsPaused = { BindTarget = GameplayClockContainer.IsPaused },
ReplayLoaded = { BindTarget = DrawableRuleset.HasReplayLoaded },
},
Expand Down Expand Up @@ -485,7 +480,7 @@ private Drawable createOverlayComponents(IWorkingBeatmap working)
OnResume = Resume,
Retries = RestartCount,
OnRetry = () => Restart(),
OnQuit = () => PerformExit(true),
OnQuit = () => PerformExitWithConfirmation(),
},
},
};
Expand Down Expand Up @@ -588,25 +583,24 @@ private IBeatmap loadPlayableBeatmap(Mod[] gameplayMods, CancellationToken cance
}

/// <summary>
/// Attempts to complete a user request to exit gameplay.
/// Attempts to complete a user request to exit gameplay, with confirmation.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>This should only be called in response to a user interaction. Exiting is not guaranteed.</item>
/// <item>This will interrupt any pending progression to the results screen, even if the transition has begun.</item>
/// </list>
///
/// This method will show the pause or fail dialog before performing an exit.
/// If a dialog is not yet displayed, the exit will be blocked and the relevant dialog will display instead.
/// </remarks>
/// <param name="showDialogFirst">
/// Whether the pause or fail dialog should be shown before performing an exit.
/// If <see langword="true"/> and a dialog is not yet displayed, the exit will be blocked and the relevant dialog will display instead.
/// </param>
/// <returns>Whether this call resulted in a final exit.</returns>
protected bool PerformExit(bool showDialogFirst)
protected bool PerformExitWithConfirmation()
{
bool pauseOrFailDialogVisible =
PauseOverlay.State.Value == Visibility.Visible || FailOverlay.State.Value == Visibility.Visible;

if (showDialogFirst && !pauseOrFailDialogVisible)
if (!pauseOrFailDialogVisible)
{
// if the fail animation is currently in progress, accelerate it (it will show the pause dialog on completion).
if (ValidForResume && GameplayState.HasFailed)
Expand All @@ -625,6 +619,22 @@ protected bool PerformExit(bool showDialogFirst)
}
}

return PerformExit();
}

/// <summary>
/// Attempts to complete a user request to exit gameplay.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>This should only be called in response to a user interaction. Exiting is not guaranteed.</item>
/// <item>This will interrupt any pending progression to the results screen, even if the transition has begun.</item>
/// </list>
/// </remarks>
/// <param name="skipTransition">Whether the exit should perform without a transition, because the screen had faded to black already.</param>
/// <returns>Whether this call resulted in a final exit.</returns>
protected bool PerformExit(bool skipTransition = false)
{
// Matching osu!stable behaviour, if the results screen is pending and the user requests an exit,
// show the results instead.
if (GameplayState.HasPassed && !isRestarting)
Expand All @@ -639,6 +649,8 @@ protected bool PerformExit(bool showDialogFirst)
// Screen may not be current if a restart has been performed.
if (this.IsCurrentScreen())
{
skipExitTransition = skipTransition;

// The actual exit is performed if
// - the pause / fail dialog was not requested
// - the pause / fail dialog was requested but is already displayed (user showing intention to exit).
Expand Down Expand Up @@ -707,9 +719,14 @@ public bool Restart(bool quickRestart = false)
// stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader.
musicController.Stop();

RestartRequested?.Invoke(quickRestart);
if (RestartRequested != null)
{
skipExitTransition = quickRestart;
RestartRequested?.Invoke(quickRestart);
return true;
}

return PerformExit(false);
return PerformExit(quickRestart);
}

/// <summary>
Expand Down Expand Up @@ -1254,10 +1271,10 @@ protected virtual Task ImportScore(Score score)
ShowUserStatistics = true,
};

private void fadeOut(bool instant = false)
private void fadeOut()
{
float fadeOutDuration = instant ? 0 : 250;
this.FadeOut(fadeOutDuration);
if (!skipExitTransition)
this.FadeOut(250);

if (this.IsCurrentScreen())
{
Expand Down

0 comments on commit 21d4076

Please sign in to comment.