From f58534f60c76e4b0f9874eb91be336a49cb62035 Mon Sep 17 00:00:00 2001 From: Terochi Date: Sun, 5 Feb 2023 16:35:11 +0100 Subject: [PATCH 1/4] Extended the length of replay at the end of map --- osu.Game/Screens/Play/Player.cs | 88 +++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf7f38cdd3..fda6bdaa8f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,8 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + beginScoreImport(); + // 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). @@ -735,14 +737,9 @@ namespace osu.Game.Screens.Play // is no chance that a user could return to the (already completed) Player instance from a child screen. 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) return; - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; if (storyboardHasOutro) @@ -756,6 +753,56 @@ namespace osu.Game.Screens.Play progressToResults(true); } + /// + /// Queue the results screen for display. + /// + /// + /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . + /// + /// Whether a minimum delay () should be added before the screen is displayed. + private void progressToResults(bool withDelay) + { + resultsDisplayDelegate?.Cancel(); + + double delay = withDelay ? RESULTS_DISPLAY_DELAY : 0; + + resultsDisplayDelegate = new ScheduledDelegate(() => + { + if (prepareScoreForDisplayTask == null) + { + beginScoreImport(); + return; + } + + if (!prepareScoreForDisplayTask.IsCompleted) + // 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); + } + + private void beginScoreImport() + { + // 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) + return; + + // Ensure we are not writing to the replay any more, as we are about to consume and store the score. + DrawableRuleset.SetRecordTarget(null); + + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + } + /// /// Asynchronously run score preparation operations (database import, online submission etc.). /// @@ -785,37 +832,6 @@ namespace osu.Game.Screens.Play return scoreCopy.ScoreInfo; } - /// - /// Queue the results screen for display. - /// - /// - /// A final display will only occur once all work is completed in . This means that even after calling this method, the results screen will never be shown until ScoreProcessor.HasCompleted becomes . - /// - /// Whether a minimum delay () should be added before the screen is displayed. - 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) { // During pause, allow global volume adjust regardless of settings. From 4f23e096d76e9ab33140b77472dd4fa6247d9b0c Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 07:59:37 +0100 Subject: [PATCH 2/4] Improved readability --- osu.Game/Screens/Play/Player.cs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fda6bdaa8f..443108cf34 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -613,6 +613,7 @@ namespace osu.Game.Screens.Play // if an exit has been requested, cancel any pending completion (the user has shown intention to exit). resultsDisplayDelegate?.Cancel(); + // import current score if possible. beginScoreImport(); // The actual exit is performed if @@ -770,7 +771,11 @@ namespace osu.Game.Screens.Play { if (prepareScoreForDisplayTask == null) { - beginScoreImport(); + // Try importing score since the task hasn't been invoked yet. + if (!beginScoreImport()) + // If the task hasn't started, the score will never be imported. + resultsDisplayDelegate?.Cancel(); + return; } @@ -790,17 +795,25 @@ namespace osu.Game.Screens.Play Scheduler.Add(resultsDisplayDelegate); } - private void beginScoreImport() + /// + /// Ends replay recording and runs only when results can be shown + /// + /// + /// Whether the task has been invoked + /// + private bool beginScoreImport() { - // 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) - return; - // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); + // 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) + return false; + prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); + + return true; } /// From 43f7665c9eabe7105bb1a7c1c2ace3784214d28f Mon Sep 17 00:00:00 2001 From: Terochi Date: Mon, 6 Feb 2023 09:49:42 +0100 Subject: [PATCH 3/4] Improved readability again --- osu.Game/Screens/Play/Player.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 443108cf34..2f81b7154a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - beginScoreImport(); + attemptScoreImport(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,8 +772,8 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!beginScoreImport()) - // If the task hasn't started, the score will never be imported. + if (!attemptScoreImport()) + // If attempt failed, trying again is unnecessary resultsDisplayDelegate?.Cancel(); return; @@ -796,12 +796,12 @@ namespace osu.Game.Screens.Play } /// - /// Ends replay recording and runs only when results can be shown + /// Attempts to run /// /// - /// Whether the task has been invoked + /// Whether the attempt was successful /// - private bool beginScoreImport() + private bool attemptScoreImport() { // Ensure we are not writing to the replay any more, as we are about to consume and store the score. DrawableRuleset.SetRecordTarget(null); From 7aaaf7fca2d598998d7c407a3b259db985484b06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Feb 2023 16:55:35 +0900 Subject: [PATCH 4/4] Combine and attempt to simplify the score import / preparation process further --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 3 + osu.Game/Screens/Play/Player.cs | 82 ++++++++++----------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index a5e442b7de..7bf0482673 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.UI { set { + if (value == recorder) + return; + if (value != null && recorder != null) throw new InvalidOperationException("Cannot attach more than one recorder"); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0825f36a0a..bc453d2151 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -276,7 +276,7 @@ namespace osu.Game.Screens.Play }, FailOverlay = new FailOverlay { - SaveReplay = prepareAndImportScore, + SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false), OnRetry = () => Restart(), OnQuit = () => PerformExit(true), }, @@ -614,7 +614,7 @@ namespace osu.Game.Screens.Play resultsDisplayDelegate?.Cancel(); // import current score if possible. - attemptScoreImport(); + prepareAndImportScoreAsync(); // The actual exit is performed if // - the pause / fail dialog was not requested @@ -772,10 +772,7 @@ namespace osu.Game.Screens.Play if (prepareScoreForDisplayTask == null) { // Try importing score since the task hasn't been invoked yet. - if (!attemptScoreImport()) - // If attempt failed, trying again is unnecessary - resultsDisplayDelegate?.Cancel(); - + prepareAndImportScoreAsync(); return; } @@ -785,6 +782,12 @@ namespace osu.Game.Screens.Play 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; @@ -796,53 +799,48 @@ namespace osu.Game.Screens.Play } /// - /// Attempts to run + /// Asynchronously run score preparation operations (database import, online submission etc.). /// - /// - /// Whether the attempt was successful - /// - private bool attemptScoreImport() + /// Whether the score should be imported even if non-passing (or the current configuration doesn't allow for it). + /// The final score. + [ItemCanBeNull] + private Task 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) - return false; + if (!canShowResults && !forceImport) + return Task.FromResult(null); - prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore); - - return true; - } - - /// - /// Asynchronously run score preparation operations (database import, online submission etc.). - /// - /// The final score. - private async Task prepareAndImportScore() - { - var scoreCopy = Score.DeepClone(); - - try + return prepareScoreForDisplayTask = Task.Run(async () => { - await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score preparation failed!"); - } + var scoreCopy = Score.DeepClone(); - try - { - await ImportScore(scoreCopy).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.Error(ex, @"Score import failed!"); - } + try + { + await PrepareScoreForResultsAsync(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score preparation failed!"); + } - return scoreCopy.ScoreInfo; + try + { + await ImportScore(scoreCopy).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex, @"Score import failed!"); + } + + return scoreCopy.ScoreInfo; + }); } protected override bool OnScroll(ScrollEvent e)