From 7e5086c8d79dd1c440a30dfde936ae496b0a1ebf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 24 Aug 2022 09:50:29 +0300 Subject: [PATCH] Fix spectator client not handling multiple watch calls properly --- osu.Game/Online/Spectator/SpectatorClient.cs | 32 ++++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 745c968992..f1ce6258d6 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -65,11 +65,12 @@ namespace osu.Game.Online.Spectator public virtual event Action? OnUserFinishedPlaying; /// - /// All users currently being watched. + /// A dictionary containing all users currently being watched, with the number of watching components for each user. /// - private readonly List watchedUsers = new List(); + private readonly Dictionary watchedUsers = new Dictionary(); private readonly BindableDictionary watchedUserStates = new BindableDictionary(); + private readonly BindableList playingUsers = new BindableList(); private readonly SpectatorState currentState = new SpectatorState(); @@ -94,12 +95,15 @@ namespace osu.Game.Online.Spectator if (connected.NewValue) { // get all the users that were previously being watched - int[] users = watchedUsers.ToArray(); + var users = new Dictionary(watchedUsers); watchedUsers.Clear(); // resubscribe to watched users. - foreach (int userId in users) - WatchUser(userId); + foreach ((int user, int watchers) in users) + { + for (int i = 0; i < watchers; i++) + WatchUser(user); + } // re-send state in case it wasn't received if (IsPlaying) @@ -121,7 +125,7 @@ namespace osu.Game.Online.Spectator if (!playingUsers.Contains(userId)) playingUsers.Add(userId); - if (watchedUsers.Contains(userId)) + if (watchedUsers.ContainsKey(userId)) watchedUserStates[userId] = state; OnUserBeganPlaying?.Invoke(userId, state); @@ -136,7 +140,7 @@ namespace osu.Game.Online.Spectator { playingUsers.Remove(userId); - if (watchedUsers.Contains(userId)) + if (watchedUsers.ContainsKey(userId)) watchedUserStates[userId] = state; OnUserFinishedPlaying?.Invoke(userId, state); @@ -232,11 +236,13 @@ namespace osu.Game.Online.Spectator { Debug.Assert(ThreadSafety.IsUpdateThread); - if (watchedUsers.Contains(userId)) + if (watchedUsers.ContainsKey(userId)) + { + watchedUsers[userId]++; return; + } - watchedUsers.Add(userId); - + watchedUsers.Add(userId, 1); WatchUserInternal(userId); } @@ -246,6 +252,12 @@ namespace osu.Game.Online.Spectator // Todo: This should not be a thing, but requires framework changes. Schedule(() => { + if (watchedUsers.TryGetValue(userId, out int watchers) && watchers > 1) + { + watchedUsers[userId]--; + return; + } + watchedUsers.Remove(userId); watchedUserStates.Remove(userId); StopWatchingUserInternal(userId);