diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs
index 4646f42d63..bbdd7a3d56 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs
@@ -215,8 +215,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState)
=> instances.Single(i => i.UserId == userId).LoadScore(spectatorGameplayState.Score);
- protected override void EndGameplay(int userId)
+ protected override void EndGameplay(int userId, SpectatorState state)
{
+ if (state.State == SpectatingUserState.Completed || state.State == SpectatingUserState.Failed)
+ return;
+
RemoveUser(userId);
var instance = instances.Single(i => i.UserId == userId);
diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs
index b530965269..a710db6d24 100644
--- a/osu.Game/Screens/Play/SoloSpectator.cs
+++ b/osu.Game/Screens/Play/SoloSpectator.cs
@@ -180,7 +180,7 @@ namespace osu.Game.Screens.Play
scheduleStart(spectatorGameplayState);
}
- protected override void EndGameplay(int userId)
+ protected override void EndGameplay(int userId, SpectatorState state)
{
scheduledStart?.Cancel();
immediateSpectatorGameplayState = null;
diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs
index 460352ee07..7b58f669a0 100644
--- a/osu.Game/Screens/Spectate/SpectatorScreen.cs
+++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs
@@ -118,8 +118,8 @@ namespace osu.Game.Screens.Spectate
break;
case NotifyDictionaryChangedAction.Remove:
- foreach ((int userId, _) in e.OldItems.AsNonNull())
- onUserStateRemoved(userId);
+ foreach ((int userId, SpectatorState state) in e.OldItems.AsNonNull())
+ onUserStateRemoved(userId, state);
break;
}
}
@@ -147,7 +147,7 @@ namespace osu.Game.Screens.Spectate
}
}
- private void onUserStateRemoved(int userId)
+ private void onUserStateRemoved(int userId, SpectatorState state)
{
if (!userMap.ContainsKey(userId))
return;
@@ -158,7 +158,7 @@ namespace osu.Game.Screens.Spectate
gameplayState.Score.Replay.HasReceivedAllFrames = true;
gameplayStates.Remove(userId);
- Schedule(() => EndGameplay(userId));
+ Schedule(() => EndGameplay(userId, state));
}
private void updateGameplayState(int userId)
@@ -212,7 +212,8 @@ namespace osu.Game.Screens.Spectate
/// Ends gameplay for a user.
///
/// The user to end gameplay for.
- protected abstract void EndGameplay(int userId);
+ /// The final user state.
+ protected abstract void EndGameplay(int userId, SpectatorState state);
///
/// Stops spectating a user.
@@ -220,7 +221,10 @@ namespace osu.Game.Screens.Spectate
/// The user to stop spectating.
protected void RemoveUser(int userId)
{
- onUserStateRemoved(userId);
+ if (!userStates.TryGetValue(userId, out var state))
+ return;
+
+ onUserStateRemoved(userId, state);
users.Remove(userId);
userMap.Remove(userId);