diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 42074ac241..729119fa36 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -71,8 +72,9 @@ namespace osu.Game.Screens.Play
}
private bool readyForPush =>
+ !playerConsumed
// don't push unless the player is completely loaded
- player?.LoadState == LoadState.Ready
+ && player?.LoadState == LoadState.Ready
// don't push if the user is hovering one of the panes, unless they are idle.
&& (IsHovered || idleTracker.IsIdle.Value)
// don't push if the user is dragging a slider or otherwise.
@@ -84,6 +86,11 @@ namespace osu.Game.Screens.Play
private Player player;
+ ///
+ /// Whether the curent player instance has been consumed via .
+ ///
+ private bool playerConsumed;
+
private LogoTrackingContainer content;
private bool hideOverlays;
@@ -179,7 +186,10 @@ namespace osu.Game.Screens.Play
contentIn();
MetadataInfo.Delay(750).FadeIn(500);
- this.Delay(1800).Schedule(pushWhenLoaded);
+
+ // after an initial delay, start the debounced load check.
+ // this will continue to execute even after resuming back on restart.
+ Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, 1800, 0));
showMuteWarningIfNeeded();
}
@@ -188,17 +198,18 @@ namespace osu.Game.Screens.Play
{
base.OnResuming(last);
- contentIn();
+ // prepare for a retry.
+ player = null;
+ playerConsumed = false;
+ cancelLoad();
- this.Delay(400).Schedule(pushWhenLoaded);
+ contentIn();
}
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
- cancelLoad();
-
BackgroundBrightnessReduction = false;
// we're moving to player, so a period of silence is upcoming.
@@ -274,6 +285,14 @@ namespace osu.Game.Screens.Play
}
}
+ private Player consumePlayer()
+ {
+ Debug.Assert(!playerConsumed);
+
+ playerConsumed = true;
+ return player;
+ }
+
private void prepareNewPlayer()
{
if (!this.IsCurrentScreen())
@@ -315,64 +334,62 @@ namespace osu.Game.Screens.Play
{
if (!this.IsCurrentScreen()) return;
- try
+ if (!readyForPush)
{
- if (!readyForPush)
+ // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
+ // if we become unready for push during the delay.
+ cancelLoad();
+ return;
+ }
+
+ // if a push has already been scheduled, no further action is required.
+ // this value is reset via cancelLoad() to allow a second usage of the same PlayerLoader screen.
+ if (scheduledPushPlayer != null)
+ return;
+
+ scheduledPushPlayer = Scheduler.AddDelayed(() =>
+ {
+ // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared).
+ var consumedPlayer = consumePlayer();
+
+ contentOut();
+
+ TransformSequence pushSequence = this.Delay(250);
+
+ // only show if the warning was created (i.e. the beatmap needs it)
+ // and this is not a restart of the map (the warning expires after first load).
+ if (epilepsyWarning?.IsAlive == true)
{
- // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
- // if we become unready for push during the delay.
- cancelLoad();
- return;
+ const double epilepsy_display_length = 3000;
+
+ pushSequence
+ .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible)
+ .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint)
+ .Delay(epilepsy_display_length)
+ .Schedule(() =>
+ {
+ epilepsyWarning.Hide();
+ epilepsyWarning.Expire();
+ })
+ .Delay(EpilepsyWarning.FADE_DURATION);
}
- if (scheduledPushPlayer != null)
- return;
-
- scheduledPushPlayer = Scheduler.AddDelayed(() =>
+ pushSequence.Schedule(() =>
{
- contentOut();
+ if (!this.IsCurrentScreen()) return;
- TransformSequence pushSequence = this.Delay(250);
+ LoadTask = null;
- // only show if the warning was created (i.e. the beatmap needs it)
- // and this is not a restart of the map (the warning expires after first load).
- if (epilepsyWarning?.IsAlive == true)
- {
- const double epilepsy_display_length = 3000;
+ // By default, we want to load the player and never be returned to.
+ // Note that this may change if the player we load requested a re-run.
+ ValidForResume = false;
- pushSequence
- .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible)
- .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint)
- .Delay(epilepsy_display_length)
- .Schedule(() =>
- {
- epilepsyWarning.Hide();
- epilepsyWarning.Expire();
- })
- .Delay(EpilepsyWarning.FADE_DURATION);
- }
-
- pushSequence.Schedule(() =>
- {
- if (!this.IsCurrentScreen()) return;
-
- LoadTask = null;
-
- // By default, we want to load the player and never be returned to.
- // Note that this may change if the player we load requested a re-run.
- ValidForResume = false;
-
- if (player.LoadedBeatmapSuccessfully)
- this.Push(player);
- else
- this.Exit();
- });
- }, 500);
- }
- finally
- {
- Schedule(pushWhenLoaded);
- }
+ if (consumedPlayer.LoadedBeatmapSuccessfully)
+ this.Push(consumedPlayer);
+ else
+ this.Exit();
+ });
+ }, 500);
}
private void cancelLoad()
@@ -390,7 +407,7 @@ namespace osu.Game.Screens.Play
if (isDisposing)
{
// if the player never got pushed, we should explicitly dispose it.
- DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose());
+ DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose());
}
}