1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-14 19:22:56 +08:00

Merge pull request #22527 from Terochi/replay-length-extension

Replay recording extension until results screen transition
This commit is contained in:
Dan Balasescu 2023-03-11 15:41:32 +09:00 committed by GitHub
commit e327993d11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 36 deletions

View File

@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.UI
{ {
set set
{ {
if (value == recorder)
return;
if (value != null && recorder != null) if (value != null && recorder != null)
throw new InvalidOperationException("Cannot attach more than one recorder"); throw new InvalidOperationException("Cannot attach more than one recorder");

View File

@ -276,7 +276,7 @@ namespace osu.Game.Screens.Play
}, },
FailOverlay = new FailOverlay FailOverlay = new FailOverlay
{ {
SaveReplay = prepareAndImportScore, SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false),
OnRetry = () => Restart(), OnRetry = () => Restart(),
OnQuit = () => PerformExit(true), OnQuit = () => PerformExit(true),
}, },
@ -613,6 +613,9 @@ namespace osu.Game.Screens.Play
// if an exit has been requested, cancel any pending completion (the user has shown intention to exit). // if an exit has been requested, cancel any pending completion (the user has shown intention to exit).
resultsDisplayDelegate?.Cancel(); resultsDisplayDelegate?.Cancel();
// import current score if possible.
prepareAndImportScoreAsync();
// The actual exit is performed if // The actual exit is performed if
// - the pause / fail dialog was not requested // - the pause / fail dialog was not requested
// - the pause / fail dialog was requested but is already displayed (user showing intention to exit). // - the pause / fail dialog was requested but is already displayed (user showing intention to exit).
@ -735,14 +738,9 @@ namespace osu.Game.Screens.Play
// is no chance that a user could return to the (already completed) Player instance from a child screen. // is no chance that a user could return to the (already completed) Player instance from a child screen.
ValidForResume = false; ValidForResume = false;
// Ensure we are not writing to the replay any more, as we are about to consume and store the score.
DrawableRuleset.SetRecordTarget(null);
if (!Configuration.ShowResults) if (!Configuration.ShowResults)
return; return;
prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore);
bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value;
if (storyboardHasOutro) if (storyboardHasOutro)
@ -756,11 +754,70 @@ namespace osu.Game.Screens.Play
progressToResults(true); progressToResults(true);
} }
/// <summary>
/// Queue the results screen for display.
/// </summary>
/// <remarks>
/// A final display will only occur once all work is completed in <see cref="PrepareScoreForResultsAsync"/>. This means that even after calling this method, the results screen will never be shown until <see cref="JudgementProcessor.HasCompleted">ScoreProcessor.HasCompleted</see> becomes <see langword="true"/>.
/// </remarks>
/// <param name="withDelay">Whether a minimum delay (<see cref="RESULTS_DISPLAY_DELAY"/>) should be added before the screen is displayed.</param>
private void progressToResults(bool withDelay)
{
resultsDisplayDelegate?.Cancel();
double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0;
resultsDisplayDelegate = new ScheduledDelegate(() =>
{
if (prepareScoreForDisplayTask == null)
{
// Try importing score since the task hasn't been invoked yet.
prepareAndImportScoreAsync();
return;
}
if (!prepareScoreForDisplayTask.IsCompleted)
// If the asynchronous preparation has not completed, keep repeating this delegate.
return;
resultsDisplayDelegate?.Cancel();
if (prepareScoreForDisplayTask.GetResultSafely() == null)
{
// If score import did not occur, we do not want to show the results screen.
return;
}
if (!this.IsCurrentScreen())
// This player instance may already be in the process of exiting.
return;
this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely()));
}, Time.Current + delay, 50);
Scheduler.Add(resultsDisplayDelegate);
}
/// <summary> /// <summary>
/// Asynchronously run score preparation operations (database import, online submission etc.). /// Asynchronously run score preparation operations (database import, online submission etc.).
/// </summary> /// </summary>
/// <param name="forceImport">Whether the score should be imported even if non-passing (or the current configuration doesn't allow for it).</param>
/// <returns>The final score.</returns> /// <returns>The final score.</returns>
private async Task<ScoreInfo> prepareAndImportScore() [ItemCanBeNull]
private Task<ScoreInfo> prepareAndImportScoreAsync(bool forceImport = false)
{
// Ensure we are not writing to the replay any more, as we are about to consume and store the score.
DrawableRuleset.SetRecordTarget(null);
if (prepareScoreForDisplayTask != null)
return prepareScoreForDisplayTask;
// We do not want to import the score in cases where we don't show results
bool canShowResults = Configuration.ShowResults && ScoreProcessor.HasCompleted.Value && GameplayState.HasPassed;
if (!canShowResults && !forceImport)
return Task.FromResult<ScoreInfo>(null);
return prepareScoreForDisplayTask = Task.Run(async () =>
{ {
var scoreCopy = Score.DeepClone(); var scoreCopy = Score.DeepClone();
@ -783,37 +840,7 @@ namespace osu.Game.Screens.Play
} }
return scoreCopy.ScoreInfo; return scoreCopy.ScoreInfo;
} });
/// <summary>
/// Queue the results screen for display.
/// </summary>
/// <remarks>
/// A final display will only occur once all work is completed in <see cref="PrepareScoreForResultsAsync"/>. This means that even after calling this method, the results screen will never be shown until <see cref="JudgementProcessor.HasCompleted">ScoreProcessor.HasCompleted</see> becomes <see langword="true"/>.
/// </remarks>
/// <param name="withDelay">Whether a minimum delay (<see cref="RESULTS_DISPLAY_DELAY"/>) should be added before the screen is displayed.</param>
private void progressToResults(bool withDelay)
{
resultsDisplayDelegate?.Cancel();
double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0;
resultsDisplayDelegate = new ScheduledDelegate(() =>
{
if (prepareScoreForDisplayTask?.IsCompleted != true)
// If the asynchronous preparation has not completed, keep repeating this delegate.
return;
resultsDisplayDelegate?.Cancel();
if (!this.IsCurrentScreen())
// This player instance may already be in the process of exiting.
return;
this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely()));
}, Time.Current + delay, 50);
Scheduler.Add(resultsDisplayDelegate);
} }
protected override bool OnScroll(ScrollEvent e) protected override bool OnScroll(ScrollEvent e)