Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix occasional flash when quick exiting / retrying from player #30623

Merged
merged 5 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading