From a4397ee68ca3be618f663efafa195154da3cd647 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 22 Oct 2021 22:07:41 +0900 Subject: [PATCH] Add playlist continuation flow --- .../Multiplayer/TestMultiplayerClient.cs | 116 +++++++++++++----- 1 file changed, 88 insertions(+), 28 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 3450cc0fc9..717f55d377 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -42,8 +42,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private readonly TestRequestHandlingMultiplayerRoomManager roomManager; - private long lastPlaylistItemId; - public TestMultiplayerClient(TestRequestHandlingMultiplayerRoomManager roomManager) { this.roomManager = roomManager; @@ -123,6 +121,8 @@ namespace osu.Game.Tests.Visual.Multiplayer ChangeRoomState(MultiplayerRoomState.Open); ((IMultiplayerClient)this).ResultsReady(); + + finishPlaylistItem().ContinueWith(_ => advanceToNextPlaylistItem()); } break; @@ -144,8 +144,6 @@ namespace osu.Game.Tests.Visual.Multiplayer if (password != apiRoom.Password.Value) throw new InvalidOperationException("Invalid password."); - lastPlaylistItemId = apiRoom.Playlist.Last().ID; - var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value @@ -157,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = apiRoom.Name.Value, MatchType = apiRoom.Type.Value, - PlaylistItemId = lastPlaylistItemId, + PlaylistItemId = apiRoom.Playlist.Single().ID, Password = password, QueueMode = apiRoom.QueueMode.Value }, @@ -193,6 +191,24 @@ namespace osu.Game.Tests.Visual.Multiplayer public override async Task ChangeSettings(MultiplayerRoomSettings settings) { Debug.Assert(Room != null); + Debug.Assert(APIRoom != null); + + switch (Room.Settings.QueueMode, settings.QueueMode) + { + case (QueueModes.HostOnly, QueueModes.HostOnly): + break; + + // Host-only is incompatible with other queueing modes, so expire all non-expired items. + case (QueueModes.HostOnly, _): + case (_, QueueModes.HostOnly): + foreach (var playlistItem in APIRoom.Playlist.Where(i => !i.Expired).ToArray()) + { + playlistItem.Expired = true; + await ((IMultiplayerClient)this).PlaylistItemChanged(new APIPlaylistItem(playlistItem)).ConfigureAwait(false); + } + + break; + } await ((IMultiplayerClient)this).SettingsChanged(settings).ConfigureAwait(false); @@ -270,32 +286,16 @@ namespace osu.Game.Tests.Visual.Multiplayer Debug.Assert(Room != null); Debug.Assert(APIRoom != null); - item.ID = lastPlaylistItemId + 1; - - switch (Room.Settings.QueueMode) + if (Room.Settings.QueueMode == QueueModes.HostOnly && APIRoom.Playlist.Count > 0) { - case QueueModes.HostOnly: - if (Room.Host?.UserID != LocalUser?.UserID) - throw new InvalidOperationException("Local user is not the room host."); + if (Room.Host?.UserID != LocalUser?.UserID) + throw new InvalidOperationException("Local user is not the room host."); - await RemovePlaylistItem(new APIPlaylistItem(APIRoom.Playlist.Single(i => i.ID == lastPlaylistItemId))).ConfigureAwait(false); - await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false); - - Room.Settings.PlaylistItemId = item.ID; - await ChangeSettings(Room.Settings).ConfigureAwait(false); - break; - - case QueueModes.FreeForAll: - await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false); - // Todo: Advance if the added item is the last non-expired one. - break; - - case QueueModes.FairRotate: - // Todo: Not implemented. - throw new NotImplementedException(); + item.ID = APIRoom.Playlist.Last().ID; + await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); } - - lastPlaylistItemId = item.ID; + else + await addPlaylistItem(item).ConfigureAwait(false); } public override Task RemovePlaylistItem(APIPlaylistItem item) @@ -343,5 +343,65 @@ namespace osu.Game.Tests.Visual.Multiplayer break; } } + + private async Task addPlaylistItem(APIPlaylistItem newItem) + { + Debug.Assert(Room != null); + Debug.Assert(APIRoom != null); + + newItem.ID = (APIRoom.Playlist.LastOrDefault()?.ID ?? 0) + 1; + await ((IMultiplayerClient)this).PlaylistItemAdded(newItem).ConfigureAwait(false); + + // A more valid selection can occur as a result of adding a new playlist item (e.g. if all previous items were expired). + await advanceToNextPlaylistItem().ConfigureAwait(false); + } + + private async Task finishPlaylistItem() + { + Debug.Assert(Room != null); + Debug.Assert(APIRoom != null); + + var currentItem = APIRoom.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId); + + // Expire the current playlist item. + await ((IMultiplayerClient)this).PlaylistItemChanged(new APIPlaylistItem(currentItem) { Expired = true }).ConfigureAwait(false); + + // In host-only mode, a duplicate playlist item will be used for the next round. + if (Room.Settings.QueueMode == QueueModes.HostOnly) + await addPlaylistItem(new APIPlaylistItem(currentItem)).ConfigureAwait(false); + } + + private async Task advanceToNextPlaylistItem() + { + Debug.Assert(Room != null); + Debug.Assert(APIRoom != null); + + long nextId = 0; + + switch (Room.Settings.QueueMode) + { + case QueueModes.HostOnly: + // Pick the single non-expired playlist item. + nextId = APIRoom.Playlist.SingleOrDefault(i => !i.Expired)?.ID ?? 0; + break; + + case QueueModes.FairRotate: + // Group playlist items by (user_id -> count_expired), and select the first available playlist item from a user that has available beatmaps where count_expired is the lowest. + throw new NotImplementedException(); + + case QueueModes.FreeForAll: + // Pick the first available non-expired playlist item, or default to the last. + nextId = APIRoom.Playlist.FirstOrDefault(i => !i.Expired)?.ID + ?? APIRoom.Playlist.LastOrDefault()?.ID + ?? 0; + break; + } + + if (nextId != Room.Settings.PlaylistItemId) + { + Room.Settings.PlaylistItemId = nextId; + await ChangeSettings(Room.Settings).ConfigureAwait(false); + } + } } }