From 89b4f695884ca229193dc8e7bd2b5d009847a55b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:39 +0900 Subject: [PATCH 1/5] Expose playing user states as bindable dictionary --- osu.Game/Online/Spectator/SpectatorClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index f930328846..810299e90d 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -39,10 +39,10 @@ namespace osu.Game.Online.Spectator private readonly List watchingUsers = new List(); public IBindableList PlayingUsers => playingUsers; - private readonly BindableList playingUsers = new BindableList(); - private readonly Dictionary playingUserStates = new Dictionary(); + public IBindableDictionary PlayingUserStates => playingUserStates; + private readonly BindableDictionary playingUserStates = new BindableDictionary(); private IBeatmap? currentBeatmap; From b515fe3cb1f9ee8732bd427dfc842cbefca3a9b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:58 +0900 Subject: [PATCH 2/5] Fix playing user state not removed on stop watching --- osu.Game/Online/Spectator/SpectatorClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 810299e90d..649af03bc4 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -183,6 +183,7 @@ namespace osu.Game.Online.Spectator public void StopWatchingUser(int userId) { watchingUsers.Remove(userId); + playingUserStates.Remove(userId); StopWatchingUserInternal(userId); } From 7ee81669f7a304982bb88cccf5cf354a9cc72131 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:27:34 +0900 Subject: [PATCH 3/5] Remove bind helpers from SpectatorClient --- osu.Game/Online/Spectator/SpectatorClient.cs | 28 -------- osu.Game/Screens/Spectate/SpectatorScreen.cs | 68 +++++++++++--------- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 649af03bc4..04904f66ac 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -238,33 +238,5 @@ namespace osu.Game.Online.Spectator lastSendTime = Time.Current; } - - /// - /// Attempts to retrieve the for a currently-playing user. - /// - /// The user. - /// The current for the user, if they're playing. null if the user is not playing. - /// true if successful (the user is playing), false otherwise. - public bool TryGetPlayingUserState(int userId, out SpectatorState state) - { - return playingUserStates.TryGetValue(userId, out state); - } - - /// - /// Bind an action to with the option of running the bound action once immediately. - /// - /// The action to perform when a user begins playing. - /// Whether the action provided in should be run once immediately for all users currently playing. - public void BindUserBeganPlaying(Action callback, bool runOnceImmediately = false) - { - // The lock is taken before the event is subscribed to to prevent doubling of events. - OnUserBeganPlaying += callback; - - if (!runOnceImmediately) - return; - - foreach (var (userId, state) in playingUserStates) - callback(userId, state); - } } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index e6c9a0acd4..0adc5b863f 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.Spectate [Resolved] private UserLookupCache userLookupCache { get; set; } + private readonly IBindableDictionary playingUserStates = new BindableDictionary(); + private readonly Dictionary userMap = new Dictionary(); private readonly Dictionary gameplayStates = new Dictionary(); @@ -65,8 +68,9 @@ namespace osu.Game.Screens.Spectate foreach (var u in users.Result) userMap[u.Id] = u; - spectatorClient.BindUserBeganPlaying(userBeganPlaying, true); - spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; + playingUserStates.BindTo(spectatorClient.PlayingUserStates); + playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); + spectatorClient.OnNewFrames += userSentFrames; managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); @@ -102,7 +106,7 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in userMap) { - if (!spectatorClient.TryGetPlayingUserState(userId, out var userState)) + if (!playingUserStates.TryGetValue(userId, out var userState)) continue; if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID)) @@ -110,7 +114,23 @@ namespace osu.Game.Screens.Spectate } } - private void userBeganPlaying(int userId, SpectatorState state) + private void onPlayingUserStatesChanged(object sender, NotifyDictionaryChangedEventArgs e) + { + switch (e.Action) + { + case NotifyDictionaryChangedAction.Add: + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; + + case NotifyDictionaryChangedAction.Remove: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + break; + } + } + + private void onUserStateAdded(int userId, SpectatorState state) { if (state.RulesetID == null || state.BeatmapID == null) return; @@ -118,24 +138,30 @@ namespace osu.Game.Screens.Spectate if (!userMap.ContainsKey(userId)) return; - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out _)) + Schedule(() => OnUserStateChanged(userId, state)); + updateGameplayState(userId); + } + + private void onUserStateRemoved(int userId) + { + if (!userMap.ContainsKey(userId)) return; - Schedule(() => OnUserStateChanged(userId, state)); + if (!gameplayStates.TryGetValue(userId, out var gameplayState)) + return; - updateGameplayState(userId); + gameplayState.Score.Replay.HasReceivedAllFrames = true; + + gameplayStates.Remove(userId); + Schedule(() => EndGameplay(userId)); } private void updateGameplayState(int userId) { Debug.Assert(userMap.ContainsKey(userId)); - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out var spectatorState)) - return; - var user = userMap[userId]; + var spectatorState = playingUserStates[userId]; var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == spectatorState.RulesetID)?.CreateInstance(); if (resolvedRuleset == null) @@ -186,20 +212,6 @@ namespace osu.Game.Screens.Spectate } } - private void userFinishedPlaying(int userId, SpectatorState state) - { - if (!userMap.ContainsKey(userId)) - return; - - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; - - gameplayState.Score.Replay.HasReceivedAllFrames = true; - - gameplayStates.Remove(userId); - Schedule(() => EndGameplay(userId)); - } - /// /// Invoked when a spectated user's state has changed. /// @@ -226,7 +238,7 @@ namespace osu.Game.Screens.Spectate /// The user to stop spectating. protected void RemoveUser(int userId) { - userFinishedPlaying(userId, null); + onUserStateRemoved(userId); userIds.Remove(userId); userMap.Remove(userId); @@ -240,8 +252,6 @@ namespace osu.Game.Screens.Spectate if (spectatorClient != null) { - spectatorClient.OnUserBeganPlaying -= userBeganPlaying; - spectatorClient.OnUserFinishedPlaying -= userFinishedPlaying; spectatorClient.OnNewFrames -= userSentFrames; foreach (var (userId, _) in userMap) From ee4bca9ed12d5f65cc55d3b24f1164851c89c746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:37:43 +0900 Subject: [PATCH 4/5] Handle collection changed event --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 0adc5b863f..9a20bb58b8 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -127,6 +127,14 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in e.OldItems.AsNonNull()) onUserStateRemoved(userId); break; + + case NotifyDictionaryChangedAction.Replace: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; } } From 0c504c3b7d0adad741bf4d6e0fe2f25538644e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:24:22 +0900 Subject: [PATCH 5/5] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 90d131b117..57550cfb93 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 587bdaf622..1e3b77cd70 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7ba7a554d6..a2a9ac35fc 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - +