diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 42305ccd81..bc0041e2c2 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -45,8 +45,6 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddRepeatStep("add some users", () => Client.AddUser(new APIUser { Id = id++ }), 5); checkPlayingUserCount(0); - AddAssert("playlist item is available", () => Client.CurrentMatchPlayingItem.Value != null); - changeState(3, MultiplayerUserState.WaitingForLoad); checkPlayingUserCount(3); @@ -64,8 +62,6 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("leave room", () => Client.LeaveRoom()); checkPlayingUserCount(0); - - AddAssert("playlist item is null", () => Client.CurrentMatchPlayingItem.Value == null); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index 8373979308..4bf38c9ff8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("first item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -27,13 +27,11 @@ namespace osu.Game.Tests.Visual.Multiplayer { addItem(() => OtherBeatmap); AddAssert("playlist has 2 items", () => Client.APIRoom?.Playlist.Count == 2); - AddAssert("last playlist item is different", () => Client.APIRoom?.Playlist[1].Beatmap.Value.OnlineID == OtherBeatmap.OnlineID); addItem(() => InitialBeatmap); AddAssert("playlist has 3 items", () => Client.APIRoom?.Playlist.Count == 3); - AddAssert("last playlist item is different", () => Client.APIRoom?.Playlist[2].Beatmap.Value.OnlineID == InitialBeatmap.OnlineID); - AddAssert("first item still selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("first item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -43,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("playlist has only one item", () => Client.APIRoom?.Playlist.Count == 1); AddAssert("playlist item is expired", () => Client.APIRoom?.Playlist[0].Expired == true); - AddAssert("last item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("last item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -55,12 +53,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); AddAssert("first item expired", () => Client.APIRoom?.Playlist[0].Expired == true); - AddAssert("next item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[1].ID); + AddAssert("next item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[1].ID); RunGameplay(); AddAssert("second item expired", () => Client.APIRoom?.Playlist[1].Expired == true); - AddAssert("next item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[2].ID); + AddAssert("next item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[2].ID); } [Test] @@ -74,8 +72,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("change queue mode", () => Client.ChangeSettings(queueMode: QueueMode.HostOnly)); AddAssert("playlist has 3 items", () => Client.APIRoom?.Playlist.Count == 3); - AddAssert("playlist item is the other beatmap", () => Client.CurrentMatchPlayingItem.Value?.BeatmapID == OtherBeatmap.OnlineID); - AddAssert("playlist item is not expired", () => Client.APIRoom?.Playlist[1].Expired == false); + AddAssert("item 2 is not expired", () => Client.APIRoom?.Playlist[1].Expired == false); + AddAssert("current item is the other beatmap", () => Client.Room?.Settings.PlaylistItemId == 2); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index ccac3de304..c06bd8fdbe 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("first item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => InitialBeatmap); - AddAssert("playlist item still selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("playlist item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => OtherBeatmap); - AddAssert("playlist item still selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[0].ID); + AddAssert("playlist item still selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[0].ID); } [Test] @@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("playlist contains two items", () => Client.APIRoom?.Playlist.Count == 2); AddAssert("first playlist item expired", () => Client.APIRoom?.Playlist[0].Expired == true); AddAssert("second playlist item not expired", () => Client.APIRoom?.Playlist[1].Expired == false); - AddAssert("second playlist item selected", () => Client.CurrentMatchPlayingItem.Value?.ID == Client.APIRoom?.Playlist[1].ID); + AddAssert("second playlist item selected", () => Client.Room?.Settings.PlaylistItemId == Client.APIRoom?.Playlist[1].ID); } [Test] @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap())); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); - AddUntilStep("selected item is new beatmap", () => Client.CurrentMatchPlayingItem.Value?.Beatmap.Value?.OnlineID == otherBeatmap.OnlineID); + AddUntilStep("selected item is new beatmap", () => (CurrentSubScreen as MultiplayerMatchSubScreen)?.SelectedItem.Value.BeatmapID == otherBeatmap.OnlineID); } private void addItem(Func beatmap) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 5eb0abc830..f20ab914e2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -416,7 +416,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Enter song select", () => { var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerScreenStack.CurrentScreen).CurrentSubScreen; - ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.CurrentMatchPlayingItem.Value); + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId); }); AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 5708b2f789..73f2ed5b39 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Multiplayer; @@ -27,7 +28,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("initialise gameplay", () => { - Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray())); + Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, new PlaylistItem + { + Beatmap = { Value = Beatmap.Value.BeatmapInfo }, + Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset } + }, Client.Room?.Users.ToArray())); }); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index a2b2da0aec..61a92c32a4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; @@ -27,11 +28,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerQueueList : MultiplayerTestScene { - private MultiplayerQueueList playlist; + private readonly Bindable selectedItem = new Bindable(); [Cached(typeof(UserLookupCache))] private readonly TestUserLookupCache userLookupCache = new TestUserLookupCache(); + private MultiplayerQueueList playlist; private BeatmapManager beatmaps; private RulesetStore rulesets; private BeatmapSetInfo importedSet; @@ -50,12 +52,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create playlist", () => { + selectedItem.Value = null; + Child = playlist = new MultiplayerQueueList { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(500, 300), - SelectedItem = { BindTarget = Client.CurrentMatchPlayingItem }, + SelectedItem = { BindTarget = selectedItem }, Items = { BindTarget = Client.APIRoom!.Playlist } }; }); @@ -107,22 +111,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set all players queue mode", () => Client.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers })); AddUntilStep("wait for queue mode change", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers); - assertDeleteButtonVisibility(0, false); - addPlaylistItem(() => API.LocalUser.Value.OnlineID); + + AddStep("select item 0", () => selectedItem.Value = playlist.ChildrenOfType>().ElementAt(0).Model); assertDeleteButtonVisibility(0, false); assertDeleteButtonVisibility(1, true); - // Run through gameplay. - AddStep("set state to ready", () => Client.ChangeUserState(API.LocalUser.Value.Id, MultiplayerUserState.Ready)); - AddUntilStep("local state is ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready); - AddStep("start match", () => Client.StartMatch()); - AddUntilStep("match started", () => Client.LocalUser?.State == MultiplayerUserState.WaitingForLoad); - AddStep("set state to loaded", () => Client.ChangeUserState(API.LocalUser.Value.Id, MultiplayerUserState.Loaded)); - AddUntilStep("local state is playing", () => Client.LocalUser?.State == MultiplayerUserState.Playing); - AddStep("set state to finished play", () => Client.ChangeUserState(API.LocalUser.Value.Id, MultiplayerUserState.FinishedPlay)); - AddUntilStep("local state is results", () => Client.LocalUser?.State == MultiplayerUserState.Results); - + AddStep("select item 1", () => selectedItem.Value = playlist.ChildrenOfType>().ElementAt(1).Model); + assertDeleteButtonVisibility(0, true); assertDeleteButtonVisibility(1, false); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 55b4def908..f366de557f 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -95,8 +95,6 @@ namespace osu.Game.Online.Multiplayer protected readonly BindableList PlayingUserIds = new BindableList(); - public readonly Bindable CurrentMatchPlayingItem = new Bindable(); - /// /// The corresponding to the local player, if available. /// @@ -162,9 +160,6 @@ namespace osu.Game.Online.Multiplayer var joinedRoom = await JoinRoom(room.RoomID.Value.Value, password ?? room.Password.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); - // Populate playlist items. - var playlistItems = await Task.WhenAll(joinedRoom.Playlist.Select(item => createPlaylistItem(item, item.ID == joinedRoom.Settings.PlaylistItemId))).ConfigureAwait(false); - // Populate users. Debug.Assert(joinedRoom.Users != null); await Task.WhenAll(joinedRoom.Users.Select(PopulateUser)).ConfigureAwait(false); @@ -176,7 +171,7 @@ namespace osu.Game.Online.Multiplayer APIRoom = room; APIRoom.Playlist.Clear(); - APIRoom.Playlist.AddRange(playlistItems); + APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(createPlaylistItem)); Debug.Assert(LocalUser != null); addUserToAPIRoom(LocalUser); @@ -219,7 +214,6 @@ namespace osu.Game.Online.Multiplayer { APIRoom = null; Room = null; - CurrentMatchPlayingItem.Value = null; PlayingUserIds.Clear(); RoomUpdated?.Invoke(); @@ -477,28 +471,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(APIRoom != null); Debug.Assert(Room != null); - Scheduler.Add(() => - { - // ensure the new selected item is populated immediately. - var playlistItem = APIRoom.Playlist.Single(p => p.ID == newSettings.PlaylistItemId); - - if (playlistItem != null) - { - GetAPIBeatmap(playlistItem.BeatmapID).ContinueWith(b => - { - // Should be called outside of the `Scheduler` logic (and specifically accessing `Exception`) to suppress an exception from firing outwards. - bool success = b.Exception == null; - - Scheduler.Add(() => - { - if (success) - playlistItem.Beatmap.Value = b.Result; - - updateLocalRoomSettings(newSettings); - }); - }); - } - }); + Scheduler.Add(() => updateLocalRoomSettings(newSettings)); return Task.CompletedTask; } @@ -653,12 +626,10 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - public async Task PlaylistItemAdded(MultiplayerPlaylistItem item) + public Task PlaylistItemAdded(MultiplayerPlaylistItem item) { if (Room == null) - return; - - var playlistItem = await createPlaylistItem(item, true).ConfigureAwait(false); + return Task.CompletedTask; Scheduler.Add(() => { @@ -668,11 +639,13 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(APIRoom != null); Room.Playlist.Add(item); - APIRoom.Playlist.Add(playlistItem); + APIRoom.Playlist.Add(createPlaylistItem(item)); ItemAdded?.Invoke(item); RoomUpdated?.Invoke(); }); + + return Task.CompletedTask; } public Task PlaylistItemRemoved(long playlistItemId) @@ -697,12 +670,10 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - public async Task PlaylistItemChanged(MultiplayerPlaylistItem item) + public Task PlaylistItemChanged(MultiplayerPlaylistItem item) { if (Room == null) - return; - - var playlistItem = await createPlaylistItem(item, true).ConfigureAwait(false); + return Task.CompletedTask; Scheduler.Add(() => { @@ -715,15 +686,13 @@ namespace osu.Game.Online.Multiplayer int existingIndex = APIRoom.Playlist.IndexOf(APIRoom.Playlist.Single(existing => existing.ID == item.ID)); APIRoom.Playlist.RemoveAt(existingIndex); - APIRoom.Playlist.Insert(existingIndex, playlistItem); - - // If the currently-selected item was the one that got replaced, update the selected item to the new one. - if (CurrentMatchPlayingItem.Value?.ID == playlistItem.ID) - CurrentMatchPlayingItem.Value = playlistItem; + APIRoom.Playlist.Insert(existingIndex, createPlaylistItem(item)); ItemChanged?.Invoke(item); RoomUpdated?.Invoke(); }); + + return Task.CompletedTask; } /// @@ -752,12 +721,11 @@ namespace osu.Game.Online.Multiplayer APIRoom.Password.Value = Room.Settings.Password; APIRoom.Type.Value = Room.Settings.MatchType; APIRoom.QueueMode.Value = Room.Settings.QueueMode; - RoomUpdated?.Invoke(); - CurrentMatchPlayingItem.Value = APIRoom.Playlist.SingleOrDefault(p => p.ID == settings.PlaylistItemId); + RoomUpdated?.Invoke(); } - private async Task createPlaylistItem(MultiplayerPlaylistItem item, bool populateBeatmapImmediately) + private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) { var ruleset = Rulesets.GetRuleset(item.RulesetID); @@ -779,9 +747,6 @@ namespace osu.Game.Online.Multiplayer playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))); playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance))); - if (populateBeatmapImmediately) - playlistItem.Beatmap.Value = await GetAPIBeatmap(item.BeatmapID).ConfigureAwait(false); - return playlistItem; } @@ -791,7 +756,7 @@ namespace osu.Game.Online.Multiplayer /// The beatmap ID. /// A token to cancel the request. /// The retrieval task. - protected abstract Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default); + public abstract Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default); /// /// For the provided user ID, update whether the user is included in . diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index d268d2bf69..f911ef3121 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -178,7 +178,7 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId); } - protected override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) + public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) { return beatmapLookupCache.GetBeatmapAsync(beatmapId, cancellationToken); } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 184ac2c563..55f5622afd 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public abstract class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner { [Cached(typeof(IBindable))] - protected readonly Bindable SelectedItem = new Bindable(); + public readonly Bindable SelectedItem = new Bindable(); public override bool? AllowTrackAdjustments => true; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 874113d859..06959d942f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -63,6 +63,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match sampleUnready = audio.Samples.Get(@"Multiplayer/player-unready"); } + protected override void LoadComplete() + { + base.LoadComplete(); + + SelectedItem.BindValueChanged(_ => updateState()); + } + protected override void OnRoomUpdated() { base.OnRoomUpdated(); @@ -104,7 +111,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match bool enableButton = Room?.State == MultiplayerRoomState.Open - && Client.CurrentMatchPlayingItem.Value?.Expired == false + && SelectedItem.Value?.ID == Room.Settings.PlaylistItemId + && !Room.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId).Expired && !operationInProgress.Value; // When the local user is the host and spectating the match, the "start match" state should be enabled if any users are ready. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 8d3686dd6d..073497e1ce 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private MultiplayerClient client { get; set; } - private readonly PlaylistItem itemToEdit; + private readonly long? itemToEdit; private LoadingLayer loadingLayer; @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// The item to be edited. May be null, in which case a new item will be added to the playlist. /// An optional initial beatmap selection to perform. /// An optional initial ruleset selection to perform. - public MultiplayerMatchSongSelect(Room room, PlaylistItem itemToEdit = null, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null) + public MultiplayerMatchSongSelect(Room room, long? itemToEdit = null, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null) : base(room) { this.itemToEdit = itemToEdit; @@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer var multiplayerItem = new MultiplayerPlaylistItem { - ID = itemToEdit?.ID ?? 0, + ID = itemToEdit ?? 0, BeatmapID = item.BeatmapID, BeatmapChecksum = item.Beatmap.Value.MD5Hash, RulesetID = item.RulesetID, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 946c749db3..3543836d8d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -67,8 +68,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadComplete(); - SelectedItem.BindTo(client.CurrentMatchPlayingItem); - BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); UserMods.BindValueChanged(onUserModsChanged); @@ -147,7 +146,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerPlaylist { RelativeSizeAxes = Axes.Both, - RequestEdit = OpenSongSelection + RequestEdit = item => OpenSongSelection(item.ID) } }, new[] @@ -224,7 +223,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// Opens the song selection screen to add or edit an item. /// /// An optional playlist item to edit. If null, a new item will be added instead. - internal void OpenSongSelection([CanBeNull] PlaylistItem itemToEdit = null) + internal void OpenSongSelection(long? itemToEdit = null) { if (!this.IsCurrentScreen()) return; @@ -389,11 +388,48 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; } + updateCurrentItem(); + addItemButton.Alpha = client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly ? 1 : 0; Scheduler.AddOnce(UpdateMods); } + private void updateCurrentItem() + { + Debug.Assert(client.Room != null); + + var expectedSelectedItem = Room.Playlist.SingleOrDefault(i => i.ID == client.Room.Settings.PlaylistItemId); + + if (expectedSelectedItem == null) + return; + + if (SelectedItem.Value?.Equals(expectedSelectedItem) == true && expectedSelectedItem.Beatmap.Value != null) + return; + + SelectedItem.Value = null; + + if (expectedSelectedItem.Beatmap.Value == null) + { + Task.Run(async () => + { + var beatmap = await client.GetAPIBeatmap(expectedSelectedItem.BeatmapID).ConfigureAwait(false); + + Schedule(() => + { + expectedSelectedItem.Beatmap.Value = beatmap; + + if (Room.Playlist.SingleOrDefault(i => i.ID == client.Room?.Settings.PlaylistItemId)?.Equals(expectedSelectedItem) == true) + applyCurrentItem(); + }); + }); + } + else + applyCurrentItem(); + + void applyCurrentItem() => SelectedItem.Value = expectedSelectedItem; + } + private void handleRoomLost() => Schedule(() => { if (this.IsCurrentScreen()) @@ -446,6 +482,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!this.IsCurrentScreen()) return; + if (client.Room == null) + return; + if (!client.IsHost) { // todo: should handle this when the request queue is implemented. @@ -454,7 +493,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; } - this.Push(new MultiplayerMatchSongSelect(Room, SelectedItem.Value, beatmap, ruleset)); + this.Push(new MultiplayerMatchSongSelect(Room, client.Room.Settings.PlaylistItemId, beatmap, ruleset)); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index d22f0415e6..d20d6b1d37 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -376,7 +376,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, playlistItemId); - protected override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) + public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) { IBeatmapSetInfo? set = roomManager.ServerSideRooms.SelectMany(r => r.Playlist) .FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value.BeatmapSet