From 0cfaef36058a374feb31d73a400e3bfc201777dc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jul 2022 11:21:52 +0300 Subject: [PATCH 1/8] Remove iOS build CI workaround --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c728d89ed1..ef729a779f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,12 +126,6 @@ jobs: with: dotnet-version: "6.0.x" - # macOS agents recently have empty NuGet config files, resulting in restore failures, - # see https://github.com/actions/virtual-environments/issues/5768 - # Add the global nuget package source manually for now. - - name: Setup NuGet.Config - run: echo '' > ~/.config/NuGet/NuGet.Config - # Contrary to seemingly any other msbuild, msbuild running on macOS/Mono # cannot accept .sln(f) files as arguments. # Build just the main game for now. From 1c2ffb3bc4414b6c8ca51d716b2d745fc53e6d5b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 18:03:26 +0900 Subject: [PATCH 2/8] Fix server-side objects being sent to client --- .../Multiplayer/TestMultiplayerClient.cs | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 6b7495762a..ff83997936 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using MessagePack; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; @@ -50,7 +52,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// private Room? serverSideAPIRoom; - private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex]; + private MultiplayerPlaylistItem? currentItem => serverSidePlaylist[currentIndex]; private int currentIndex; private long lastPlaylistItemId; @@ -79,7 +81,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(MultiplayerRoomUser user) { - ((IMultiplayerClient)this).UserJoined(user).WaitSafely(); + ((IMultiplayerClient)this).UserJoined(clone(user)).WaitSafely(); // We want the user to be immediately available for testing, so force a scheduler update to run the update-bound continuation. Scheduler.Update(); @@ -93,7 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer .Select(team => (teamID: team.ID, userCount: Room.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))) .OrderBy(pair => pair.userCount) .First().teamID; - ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState { TeamID = bestTeam }).WaitSafely(); + ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(new TeamVersusUserState { TeamID = bestTeam })).WaitSafely(); break; } } @@ -102,7 +104,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Debug.Assert(Room != null); - ((IMultiplayerClient)this).UserLeft(new MultiplayerRoomUser(user.Id)); + ((IMultiplayerClient)this).UserLeft(clone(new MultiplayerRoomUser(user.Id))); Schedule(() => { @@ -113,12 +115,12 @@ namespace osu.Game.Tests.Visual.Multiplayer public void ChangeRoomState(MultiplayerRoomState newState) { - ((IMultiplayerClient)this).RoomStateChanged(newState); + ((IMultiplayerClient)this).RoomStateChanged(clone(newState)); } public void ChangeUserState(int userId, MultiplayerUserState newState) { - ((IMultiplayerClient)this).UserStateChanged(userId, newState); + ((IMultiplayerClient)this).UserStateChanged(clone(userId), clone(newState)); updateRoomStateIfRequired(); } @@ -176,7 +178,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBeatmapAvailability) { - ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); + ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(newBeatmapAvailability)); } protected override async Task JoinRoom(long roomId, string? password = null) @@ -205,7 +207,7 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = serverSideAPIRoom.QueueMode.Value, AutoStartDuration = serverSideAPIRoom.AutoStartDuration.Value }, - Playlist = serverSidePlaylist.ToList(), + Playlist = serverSidePlaylist, Users = { localUser }, Host = localUser }; @@ -216,7 +218,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomSetupAction?.Invoke(room); RoomSetupAction = null; - return room; + return clone(room); } protected override void OnRoomJoined() @@ -236,13 +238,13 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); + public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(clone(userId)); public override Task KickUser(int userId) { Debug.Assert(Room != null); - return ((IMultiplayerClient)this).UserKicked(Room.Users.Single(u => u.UserID == userId)); + return ((IMultiplayerClient)this).UserKicked(clone(Room.Users.Single(u => u.UserID == userId))); } public override async Task ChangeSettings(MultiplayerRoomSettings settings) @@ -256,7 +258,7 @@ namespace osu.Game.Tests.Visual.Multiplayer await changeQueueMode(settings.QueueMode).ConfigureAwait(false); - await ((IMultiplayerClient)this).SettingsChanged(settings).ConfigureAwait(false); + await ((IMultiplayerClient)this).SettingsChanged(clone(settings)).ConfigureAwait(false); foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.Idle); @@ -285,7 +287,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void ChangeUserMods(int userId, IEnumerable newMods) { - ((IMultiplayerClient)this).UserModsChanged(userId, newMods.ToList()); + ((IMultiplayerClient)this).UserModsChanged(clone(userId), clone(newMods)); } public override Task ChangeUserMods(IEnumerable newMods) @@ -312,7 +314,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { userState.TeamID = targetTeam.ID; - await ((IMultiplayerClient)this).MatchUserStateChanged(LocalUser.UserID, userState).ConfigureAwait(false); + await ((IMultiplayerClient)this).MatchUserStateChanged(clone(LocalUser.UserID), clone(userState)).ConfigureAwait(false); } break; @@ -382,7 +384,7 @@ namespace osu.Game.Tests.Visual.Multiplayer serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item; serverSideAPIRoom.Playlist[serverSideAPIRoom.Playlist.IndexOf(serverSideAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); - await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); + await ((IMultiplayerClient)this).PlaylistItemChanged(clone(item)).ConfigureAwait(false); } public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, item); @@ -409,7 +411,7 @@ namespace osu.Game.Tests.Visual.Multiplayer serverSidePlaylist.Remove(item); serverSideAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); - await ((IMultiplayerClient)this).PlaylistItemRemoved(playlistItemId).ConfigureAwait(false); + await ((IMultiplayerClient)this).PlaylistItemRemoved(clone(playlistItemId)).ConfigureAwait(false); await updateCurrentItem(Room).ConfigureAwait(false); updateRoomStateIfRequired(); @@ -427,14 +429,14 @@ namespace osu.Game.Tests.Visual.Multiplayer await ((IMultiplayerClient)this).MatchRoomStateChanged(null).ConfigureAwait(false); foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, null).ConfigureAwait(false); + await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), null).ConfigureAwait(false); break; case MatchType.TeamVersus: - await ((IMultiplayerClient)this).MatchRoomStateChanged(TeamVersusRoomState.CreateDefault()).ConfigureAwait(false); + await ((IMultiplayerClient)this).MatchRoomStateChanged(clone(TeamVersusRoomState.CreateDefault())).ConfigureAwait(false); foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(user.UserID, new TeamVersusUserState()).ConfigureAwait(false); + await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(new TeamVersusUserState())).ConfigureAwait(false); break; } } @@ -463,7 +465,7 @@ namespace osu.Game.Tests.Visual.Multiplayer currentItem.Expired = true; currentItem.PlayedAt = DateTimeOffset.Now; - await ((IMultiplayerClient)this).PlaylistItemChanged(currentItem).ConfigureAwait(false); + await ((IMultiplayerClient)this).PlaylistItemChanged(clone(currentItem)).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false); // In host-only mode, a duplicate playlist item will be used for the next round. @@ -496,7 +498,7 @@ namespace osu.Game.Tests.Visual.Multiplayer serverSidePlaylist.Add(item); serverSideAPIRoom.Playlist.Add(new PlaylistItem(item)); - await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false); + await ((IMultiplayerClient)this).PlaylistItemAdded(clone(item)).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false); } @@ -514,7 +516,7 @@ namespace osu.Game.Tests.Visual.Multiplayer room.Settings.PlaylistItemId = nextItem.ID; if (notify && nextItem.ID != lastItem) - await ((IMultiplayerClient)this).SettingsChanged(room.Settings).ConfigureAwait(false); + await ((IMultiplayerClient)this).SettingsChanged(clone(room.Settings)).ConfigureAwait(false); } private async Task updatePlaylistOrder(MultiplayerRoom room) @@ -564,12 +566,18 @@ namespace osu.Game.Tests.Visual.Multiplayer item.PlaylistOrder = (ushort)i; - await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); + await ((IMultiplayerClient)this).PlaylistItemChanged(clone(item)).ConfigureAwait(false); } // Also ensure that the API room's playlist is correct. foreach (var item in serverSideAPIRoom.Playlist) item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder; } + + private T clone(T incoming) + { + byte[]? serialized = MessagePackSerializer.Serialize(typeof(T), incoming, SignalRUnionWorkaroundResolver.OPTIONS); + return MessagePackSerializer.Deserialize(serialized, SignalRUnionWorkaroundResolver.OPTIONS); + } } } From 0be858b5bf7acbc3906f4c9172a16e4920e2791e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 18:04:29 +0900 Subject: [PATCH 3/8] Rename APIRoom -> ClientAPIRoom, remove unused asserts --- .../Visual/Multiplayer/QueueModeTestScene.cs | 2 +- .../TestSceneAllPlayersQueueMode.cs | 26 +++++++++---------- .../Multiplayer/TestSceneHostOnlyQueueMode.cs | 24 ++++++++--------- .../Multiplayer/TestSceneMultiplayer.cs | 12 ++++----- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- .../TestSceneMultiplayerQueueList.cs | 8 +++--- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 4 +-- .../Multiplayer/TestMultiplayerClient.cs | 10 +------ 9 files changed, 41 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 074a92f5b0..53bed75723 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestCreatedWithCorrectMode() { - AddAssert("room created with correct mode", () => MultiplayerClient.APIRoom?.QueueMode.Value == Mode); + AddAssert("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode); } protected void RunGameplay() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index 32fd7724e0..b08102df8c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -30,19 +30,19 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] public void TestItemAddedToTheEndOfQueue() { addItem(() => OtherBeatmap); - AddAssert("playlist has 2 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2); + AddAssert("playlist has 2 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); addItem(() => InitialBeatmap); - AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3); + AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); - AddAssert("first item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("first item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -50,9 +50,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { RunGameplay(); - AddAssert("playlist has only one item", () => MultiplayerClient.APIRoom?.Playlist.Count == 1); - AddAssert("playlist item is expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true); - AddAssert("last item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("playlist has only one item", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 1); + AddAssert("playlist item is expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddAssert("last item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -63,13 +63,13 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); - AddAssert("first item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true); - AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID); + AddAssert("first item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); RunGameplay(); - AddAssert("second item expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == true); - AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[2].ID); + AddAssert("second item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == true); + AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[2].ID); } [Test] @@ -82,8 +82,8 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(queueMode: QueueMode.HostOnly)); - AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3); - AddAssert("item 2 is not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false); + AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); + AddAssert("item 2 is not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); AddAssert("current item is the other beatmap", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index e436f418ab..8f19590731 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => InitialBeatmap); - AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => OtherBeatmap); - AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID); + AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -48,10 +48,10 @@ namespace osu.Game.Tests.Visual.Multiplayer { RunGameplay(); - AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2); - AddAssert("first playlist item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true); - AddAssert("second playlist item not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false); - AddAssert("second playlist item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID); + AddAssert("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); + AddAssert("first playlist item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddAssert("second playlist item not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); + AddAssert("second playlist item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); } [Test] @@ -60,12 +60,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); IBeatmapInfo firstBeatmap = null; - AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.APIRoom?.Playlist[0].Beatmap); + AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ClientAPIRoom?.Playlist[0].Beatmap); selectNewItem(() => OtherBeatmap); - AddAssert("first playlist item hasn't changed", () => MultiplayerClient.APIRoom?.Playlist[0].Beatmap == firstBeatmap); - AddAssert("second playlist item changed", () => MultiplayerClient.APIRoom?.Playlist[1].Beatmap != firstBeatmap); + AddAssert("first playlist item hasn't changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Beatmap == firstBeatmap); + AddAssert("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap); } [Test] @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = QueueMode.AllPlayers }).WaitSafely()); - AddUntilStep("api room updated", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers); + AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); } [Test] @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { addItem(() => OtherBeatmap); - AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2); + AddAssert("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); } private void selectNewItem(Func beatmap) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index d464527976..37cc803daf 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -243,8 +243,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1); - AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); + AddAssert("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddAssert("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] @@ -303,8 +303,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); - AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1); - AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); + AddAssert("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddAssert("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] @@ -323,7 +323,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddAssert("room has password", () => multiplayerClient.APIRoom?.Password.Value == "password"); + AddAssert("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password"); } [Test] @@ -377,7 +377,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2")); - AddUntilStep("local password changed", () => multiplayerClient.APIRoom?.Password.Value == "password2"); + AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password2"); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index bb7587ac56..478c8d3318 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -254,7 +254,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("last playlist item selected", () => { - var lastItem = this.ChildrenOfType().Single(p => p.Item.ID == MultiplayerClient.APIRoom?.Playlist.Last().ID); + var lastItem = this.ChildrenOfType().Single(p => p.Item.ID == MultiplayerClient.ClientAPIRoom?.Playlist.Last().ID); return lastItem.IsSelectedItem; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 042a9297eb..300980a53f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("initialise gameplay", () => { - Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.APIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) + Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ClientAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) { RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID, }, MultiplayerClient.Room?.Users.ToArray())); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index fcd6dd5bd2..8aa7d5aec2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(500, 300), - Items = { BindTarget = MultiplayerClient.APIRoom!.Playlist } + Items = { BindTarget = MultiplayerClient.ClientAPIRoom!.Playlist } }; }); @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestDeleteButtonAlwaysVisibleForHost() { AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); - AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers); + AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); addPlaylistItem(() => API.LocalUser.Value.OnlineID); assertDeleteButtonVisibility(1, true); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestDeleteButtonOnlyVisibleForItemOwnerIfNotHost() { AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); - AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers); + AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); AddStep("join other user", () => MultiplayerClient.AddUser(new APIUser { Id = 1234 })); AddStep("set other user as host", () => MultiplayerClient.TransferHost(1234)); @@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestCurrentItemDoesNotHaveDeleteButton() { AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely()); - AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers); + AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); addPlaylistItem(() => API.LocalUser.Value.OnlineID); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 2e39449f64..7d93e550d0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -133,14 +133,14 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("match type head to head", () => multiplayerClient.APIRoom?.Type.Value == MatchType.HeadToHead); + AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.HeadToHead); AddStep("change match type", () => multiplayerClient.ChangeSettings(new MultiplayerRoomSettings { MatchType = MatchType.TeamVersus }).WaitSafely()); - AddUntilStep("api room updated to team versus", () => multiplayerClient.APIRoom?.Type.Value == MatchType.TeamVersus); + AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.TeamVersus); } [Test] diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index ff83997936..860cf7dee9 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// The local client's . This is not always equivalent to the server-side room. /// - public new Room? APIRoom => base.APIRoom; + public Room? ClientAPIRoom => APIRoom; public Action? RoomSetupAction; @@ -126,8 +126,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private void updateRoomStateIfRequired() { - Debug.Assert(APIRoom != null); - Schedule(() => { Debug.Assert(Room != null); @@ -223,7 +221,6 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override void OnRoomJoined() { - Debug.Assert(APIRoom != null); Debug.Assert(Room != null); // emulate the server sending this after the join room. scheduler required to make sure the join room event is fired first (in Join). @@ -250,7 +247,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public override async Task ChangeSettings(MultiplayerRoomSettings settings) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); // Server is authoritative for the time being. @@ -344,7 +340,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); if (Room.Settings.QueueMode == QueueMode.HostOnly && Room.Host?.UserID != LocalUser?.UserID) @@ -392,7 +387,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task RemoveUserPlaylistItem(int userId, long playlistItemId) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(serverSideAPIRoom != null); var item = serverSidePlaylist.Find(i => i.ID == playlistItemId); @@ -444,7 +438,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task changeQueueMode(QueueMode newMode) { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); // When changing to host-only mode, ensure that at least one non-expired playlist item exists by duplicating the current item. @@ -458,7 +451,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task FinishCurrentItem() { Debug.Assert(Room != null); - Debug.Assert(APIRoom != null); Debug.Assert(currentItem != null); // Expire the current playlist item. From b64c0d011c877332d11c19170e620bd2f8a849a3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 18:58:22 +0900 Subject: [PATCH 4/8] Isolate client's Room from TestMultiplayerClient --- .../StatefulMultiplayerClientTest.cs | 10 +- .../TestSceneAllPlayersQueueMode.cs | 12 +- .../Multiplayer/TestSceneHostOnlyQueueMode.cs | 8 +- .../Multiplayer/TestSceneMultiplayer.cs | 64 ++-- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../TestSceneMultiplayerParticipantsList.cs | 8 +- .../Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- .../TestSceneMultiplayerQueueList.cs | 2 +- .../TestSceneMultiplayerSpectateButton.cs | 4 +- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 16 +- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- .../Multiplayer/TestMultiplayerClient.cs | 335 ++++++++++-------- 12 files changed, 260 insertions(+), 205 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 9c8b977ad9..0bcb38327e 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer var user = new APIUser { Id = 33 }; AddRepeatStep("add user multiple times", () => MultiplayerClient.AddUser(user), 3); - AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2); + AddUntilStep("room has 2 users", () => MultiplayerClient.ClientRoom?.Users.Count == 2); } [Test] @@ -33,10 +33,10 @@ namespace osu.Game.Tests.NonVisual.Multiplayer var user = new APIUser { Id = 44 }; AddStep("add user", () => MultiplayerClient.AddUser(user)); - AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2); + AddUntilStep("room has 2 users", () => MultiplayerClient.ClientRoom?.Users.Count == 2); AddRepeatStep("remove user multiple times", () => MultiplayerClient.RemoveUser(user), 3); - AddAssert("room has 1 user", () => MultiplayerClient.Room?.Users.Count == 1); + AddUntilStep("room has 1 user", () => MultiplayerClient.ClientRoom?.Users.Count == 1); } [Test] @@ -59,7 +59,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer changeState(6, MultiplayerUserState.WaitingForLoad); checkPlayingUserCount(6); - AddStep("another user left", () => MultiplayerClient.RemoveUser((MultiplayerClient.Room?.Users.Last().User).AsNonNull())); + AddStep("another user left", () => MultiplayerClient.RemoveUser((MultiplayerClient.ServerRoom?.Users.Last().User).AsNonNull())); checkPlayingUserCount(5); AddStep("leave room", () => MultiplayerClient.LeaveRoom()); @@ -103,7 +103,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer { for (int i = 0; i < userCount; ++i) { - int userId = MultiplayerClient.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!"); + int userId = MultiplayerClient.ServerRoom?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!"); MultiplayerClient.ChangeUserState(userId, state); } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index b08102df8c..a94a60f12d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("first item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer addItem(() => InitialBeatmap); AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); - AddAssert("first item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("first item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("playlist has only one item", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 1); AddAssert("playlist item is expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); - AddAssert("last item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("last item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -64,12 +64,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); AddAssert("first item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); - AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); + AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); RunGameplay(); AddAssert("second item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == true); - AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[2].ID); + AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[2].ID); } [Test] @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(queueMode: QueueMode.HostOnly)); AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); AddAssert("item 2 is not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); - AddAssert("current item is the other beatmap", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2); + AddUntilStep("current item is the other beatmap", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == 2); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index 8f19590731..15a1354c98 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFirstItemSelectedByDefault() { - AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("first item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => InitialBeatmap); - AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("playlist item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { selectNewItem(() => OtherBeatmap); - AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); + AddUntilStep("playlist item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } [Test] @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); AddAssert("first playlist item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); AddAssert("second playlist item not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); - AddAssert("second playlist item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); + AddUntilStep("second playlist item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 37cc803daf..193d1d4248 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -116,25 +116,25 @@ namespace osu.Game.Tests.Visual.Multiplayer // all ready AddUntilStep("all players ready", () => { - var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerClient.ClientRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); - return multiplayerClient.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; + return multiplayerClient.ClientRoom?.Users.All(u => u.State == MultiplayerUserState.Ready) == true; }); AddStep("unready all players at once", () => { - Debug.Assert(multiplayerClient.Room != null); + Debug.Assert(multiplayerClient.ServerRoom != null); - foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Idle); + foreach (var u in multiplayerClient.ServerRoom.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Idle); }); AddStep("ready all players at once", () => { - Debug.Assert(multiplayerClient.Room != null); + Debug.Assert(multiplayerClient.ServerRoom != null); - foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Ready); + foreach (var u in multiplayerClient.ServerRoom.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Ready); }); } @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void removeLastUser() { - APIUser lastUser = multiplayerClient.Room?.Users.Last().User; + APIUser lastUser = multiplayerClient.ServerRoom?.Users.Last().User; if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User) return; @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void kickLastUser() { - APIUser lastUser = multiplayerClient.Room?.Users.Last().User; + APIUser lastUser = multiplayerClient.ServerRoom?.Users.Last().User; if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User) return; @@ -166,14 +166,14 @@ namespace osu.Game.Tests.Visual.Multiplayer private void markNextPlayerReady() { - var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); + var nextUnready = multiplayerClient.ServerRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle); if (nextUnready != null) multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready); } private void markNextPlayerIdle() { - var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready); + var nextUnready = multiplayerClient.ServerRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready); if (nextUnready != null) multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle); } @@ -421,22 +421,22 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Enter song select", () => { var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; - ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId); + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId); }); AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); - AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID); + AddUntilStep("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().BeatmapID); AddStep("Select next beatmap", () => InputManager.Key(Key.Down)); - AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != multiplayerClient.Room?.Playlist.First().BeatmapID); + AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != multiplayerClient.ClientRoom?.Playlist.First().BeatmapID); AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely()); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); - AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID); + AddUntilStep("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().BeatmapID); } [Test] @@ -459,22 +459,22 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Enter song select", () => { var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; - ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId); + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId); }); AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); - AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID); + AddUntilStep("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().RulesetID); AddStep("Switch ruleset", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Ruleset.Value = new CatchRuleset().RulesetInfo); - AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != multiplayerClient.Room?.Playlist.First().RulesetID); + AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != multiplayerClient.ClientRoom?.Playlist.First().RulesetID); AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely()); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); - AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID); + AddUntilStep("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().RulesetID); } [Test] @@ -497,25 +497,25 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Enter song select", () => { var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; - ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId); + ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId); }); AddUntilStep("wait for song select", () => this.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); - AddAssert("Mods match current item", - () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddUntilStep("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() }); - AddAssert("Mods don't match current item", - () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddUntilStep("Mods don't match current item", + () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely()); AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); - AddAssert("Mods match current item", - () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); + AddUntilStep("Mods match current item", + () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym))); } [Test] @@ -890,7 +890,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RulesetID = new OsuRuleset().RulesetInfo.OnlineID, })).WaitSafely()); - AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2); + AddUntilStep("item arrived in playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 2); AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); AddUntilStep("queue contains item", () => this.ChildrenOfType().Single().Items.Single().ID == 2); @@ -921,10 +921,10 @@ namespace osu.Game.Tests.Visual.Multiplayer RulesetID = new OsuRuleset().RulesetInfo.OnlineID, })).WaitSafely()); - AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2); + AddUntilStep("item arrived in playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 2); AddStep("delete item as other user", () => multiplayerClient.RemoveUserPlaylistItem(1234, 2).WaitSafely()); - AddUntilStep("item removed from playlist", () => multiplayerClient.Room?.Playlist.Count == 1); + AddUntilStep("item removed from playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 1); AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); AddUntilStep("queue is empty", () => this.ChildrenOfType().Single().Items.Count == 0); @@ -957,7 +957,7 @@ namespace osu.Game.Tests.Visual.Multiplayer runGameplay(); AddStep("exit gameplay for other user", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Idle)); - AddUntilStep("wait for room to be idle", () => multiplayerClient.Room?.State == MultiplayerRoomState.Open); + AddUntilStep("wait for room to be idle", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Open); runGameplay(); @@ -969,9 +969,9 @@ namespace osu.Game.Tests.Visual.Multiplayer multiplayerClient.StartMatch().WaitSafely(); }); - AddUntilStep("wait for loading", () => multiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad); + AddUntilStep("wait for loading", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad); AddStep("set player loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded)); - AddUntilStep("wait for gameplay to start", () => multiplayerClient.Room?.State == MultiplayerRoomState.Playing); + AddUntilStep("wait for gameplay to start", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Playing); AddUntilStep("wait for local user to enter spectator", () => multiplayerComponents.CurrentScreen is MultiSpectatorScreen); } } @@ -996,7 +996,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click ready button", () => { - user = playingUserId == null ? multiplayerClient.LocalUser : multiplayerClient.Room?.Users.Single(u => u.UserID == playingUserId); + user = playingUserId == null ? multiplayerClient.LocalUser : multiplayerClient.ServerRoom?.Users.Single(u => u.UserID == playingUserId); lastState = user?.State ?? MultiplayerUserState.Idle; InputManager.MoveMouseTo(readyButton); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 478c8d3318..f9eec6b728 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); - AddUntilStep("match started", () => MultiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad); + AddUntilStep("match started", () => MultiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 75e3da05d3..7db18d1127 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -53,14 +53,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1); AddStep("add non-resolvable user", () => MultiplayerClient.TestAddUnresolvedUser()); - AddAssert("null user added", () => MultiplayerClient.Room.AsNonNull().Users.Count(u => u.User == null) == 1); + AddUntilStep("null user added", () => MultiplayerClient.ClientRoom.AsNonNull().Users.Count(u => u.User == null) == 1); AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); AddStep("kick null user", () => this.ChildrenOfType().Single(p => p.User.User == null) .ChildrenOfType().Single().TriggerClick()); - AddAssert("null user kicked", () => MultiplayerClient.Room.AsNonNull().Users.Count == 1); + AddUntilStep("null user kicked", () => MultiplayerClient.ClientRoom.AsNonNull().Users.Count == 1); } [Test] @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("remove host", () => MultiplayerClient.RemoveUser(API.LocalUser.Value)); - AddAssert("single panel is for second user", () => this.ChildrenOfType().Single().User.User == secondUser); + AddAssert("single panel is for second user", () => this.ChildrenOfType().Single().User.UserID == secondUser.Id); } [Test] @@ -217,7 +217,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("kick second user", () => this.ChildrenOfType().Single(d => d.IsPresent).TriggerClick()); - AddAssert("second user kicked", () => MultiplayerClient.Room?.Users.Single().UserID == API.LocalUser.Value.Id); + AddUntilStep("second user kicked", () => MultiplayerClient.ClientRoom?.Users.Single().UserID == API.LocalUser.Value.Id); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 300980a53f..9fee771b97 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ClientAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) { RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID, - }, MultiplayerClient.Room?.Users.ToArray())); + }, MultiplayerClient.ServerRoom?.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 8aa7d5aec2..e709a955b3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertDeleteButtonVisibility(1, true); AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely()); - AddUntilStep("wait for next item to be selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2); + AddUntilStep("wait for next item to be selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == 2); AddUntilStep("wait for two items in playlist", () => playlist.ChildrenOfType().Count() == 2); assertDeleteButtonVisibility(0, false); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 486da48449..91c87548c7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -99,10 +99,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestToggleWhenIdle(MultiplayerUserState initialState) { ClickButtonWhenEnabled(); - AddUntilStep("user is spectating", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Spectating); + AddUntilStep("user is spectating", () => MultiplayerClient.ClientRoom?.Users[0].State == MultiplayerUserState.Spectating); ClickButtonWhenEnabled(); - AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle", () => MultiplayerClient.ClientRoom?.Users[0].State == MultiplayerUserState.Idle); } [TestCase(MultiplayerRoomState.Closed)] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 7d93e550d0..d80537a2e5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -76,8 +76,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus); - AddAssert("user state arrived", () => multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState); + AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus); + AddUntilStep("user state arrived", () => multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState); } [Test] @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); + AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); AddStep("add another user", () => multiplayerClient.AddUser(new APIUser { Username = "otheruser", Id = 44 })); AddStep("press own button", () => @@ -104,17 +104,17 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); - AddAssert("user on team 1", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1); + AddUntilStep("user on team 1", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1); AddStep("press own button again", () => InputManager.Click(MouseButton.Left)); - AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); + AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); AddStep("press other user's button", () => { InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().ElementAt(1)); InputManager.Click(MouseButton.Left); }); - AddAssert("user still on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); + AddUntilStep("user still on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); } [Test] @@ -158,13 +158,13 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("room type is head to head", () => multiplayerClient.Room?.Settings.MatchType == MatchType.HeadToHead); + AddUntilStep("room type is head to head", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.HeadToHead); AddUntilStep("team displays are not displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam == null)); AddStep("change to team vs", () => multiplayerClient.ChangeSettings(matchType: MatchType.TeamVersus)); - AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus); + AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus); AddUntilStep("team displays are displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam != null)); } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 22f474ed42..9832acb140 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -413,7 +413,7 @@ namespace osu.Game.Online.Multiplayer UserJoined?.Invoke(user); RoomUpdated?.Invoke(); - }); + }, false); } Task IMultiplayerClient.UserLeft(MultiplayerRoomUser user) => diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 860cf7dee9..3165a4ecff 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -31,7 +31,30 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// The local client's . This is not always equivalent to the server-side room. /// - public Room? ClientAPIRoom => APIRoom; + public Room? ClientAPIRoom => base.APIRoom; + + /// + /// The local client's . This is not always equivalent to the server-side room. + /// + public MultiplayerRoom? ClientRoom => base.Room; + + /// + /// The server's . This is always up-to-date. + /// + public Room? ServerAPIRoom { get; private set; } + + /// + /// The server's . This is always up-to-date. + /// + public MultiplayerRoom? ServerRoom { get; private set; } + + [Obsolete] + protected new Room APIRoom => throw new InvalidOperationException($"Accessing the client-side API room via {nameof(TestMultiplayerClient)} is unsafe. " + + $"Use {nameof(ClientAPIRoom)} if this was intended."); + + [Obsolete] + public new MultiplayerRoom Room => throw new InvalidOperationException($"Accessing the client-side room via {nameof(TestMultiplayerClient)} is unsafe. " + + $"Use {nameof(ClientRoom)} if this was intended."); public Action? RoomSetupAction; @@ -42,17 +65,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private readonly TestMultiplayerRoomManager roomManager; - /// - /// Guaranteed up-to-date playlist. - /// - private readonly List serverSidePlaylist = new List(); - - /// - /// Guaranteed up-to-date API room. - /// - private Room? serverSideAPIRoom; - - private MultiplayerPlaylistItem? currentItem => serverSidePlaylist[currentIndex]; + private MultiplayerPlaylistItem? currentItem => ServerRoom?.Playlist[currentIndex]; private int currentIndex; private long lastPlaylistItemId; @@ -81,150 +94,160 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(MultiplayerRoomUser user) { + Debug.Assert(ServerRoom != null); + + ServerRoom.Users.Add(user); ((IMultiplayerClient)this).UserJoined(clone(user)).WaitSafely(); - // We want the user to be immediately available for testing, so force a scheduler update to run the update-bound continuation. - Scheduler.Update(); - - switch (Room?.MatchState) + switch (ServerRoom?.MatchState) { case TeamVersusRoomState teamVersus: // simulate the server's automatic assignment of users to teams on join. // the "best" team is the one with the least users on it. int bestTeam = teamVersus.Teams - .Select(team => (teamID: team.ID, userCount: Room.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))) + .Select(team => (teamID: team.ID, userCount: ServerRoom.Users.Count(u => (u.MatchState as TeamVersusUserState)?.TeamID == team.ID))) .OrderBy(pair => pair.userCount) .First().teamID; - ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(new TeamVersusUserState { TeamID = bestTeam })).WaitSafely(); + + user.MatchState = new TeamVersusUserState { TeamID = bestTeam }; + ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(user.MatchState)).WaitSafely(); break; } } public void RemoveUser(APIUser user) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); + ServerRoom.Users.Remove(ServerRoom.Users.Single(u => u.UserID == user.Id)); ((IMultiplayerClient)this).UserLeft(clone(new MultiplayerRoomUser(user.Id))); - Schedule(() => - { - if (Room.Users.Any()) - TransferHost(Room.Users.First().UserID); - }); + if (ServerRoom.Users.Any()) + TransferHost(ServerRoom.Users.First().UserID); } public void ChangeRoomState(MultiplayerRoomState newState) { - ((IMultiplayerClient)this).RoomStateChanged(clone(newState)); + Debug.Assert(ServerRoom != null); + + ServerRoom.State = clone(newState); + + ((IMultiplayerClient)this).RoomStateChanged(clone(ServerRoom.State)); } public void ChangeUserState(int userId, MultiplayerUserState newState) { - ((IMultiplayerClient)this).UserStateChanged(clone(userId), clone(newState)); + Debug.Assert(ServerRoom != null); + + var user = ServerRoom.Users.Single(u => u.UserID == userId); + user.State = clone(newState); + + ((IMultiplayerClient)this).UserStateChanged(clone(userId), clone(user.State)); + updateRoomStateIfRequired(); } private void updateRoomStateIfRequired() { - Schedule(() => + Debug.Assert(ServerRoom != null); + + switch (ServerRoom.State) { - Debug.Assert(Room != null); + case MultiplayerRoomState.Open: + break; - switch (Room.State) - { - case MultiplayerRoomState.Open: - break; + case MultiplayerRoomState.WaitingForLoad: + if (ServerRoom.Users.All(u => u.State != MultiplayerUserState.WaitingForLoad)) + { + var loadedUsers = ServerRoom.Users.Where(u => u.State == MultiplayerUserState.Loaded).ToArray(); - case MultiplayerRoomState.WaitingForLoad: - if (Room.Users.All(u => u.State != MultiplayerUserState.WaitingForLoad)) + if (loadedUsers.Length == 0) { - var loadedUsers = Room.Users.Where(u => u.State == MultiplayerUserState.Loaded).ToArray(); - - if (loadedUsers.Length == 0) - { - // all users have bailed from the load sequence. cancel the game start. - ChangeRoomState(MultiplayerRoomState.Open); - return; - } - - foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.Loaded)) - ChangeUserState(u.UserID, MultiplayerUserState.Playing); - - ((IMultiplayerClient)this).GameplayStarted(); - - ChangeRoomState(MultiplayerRoomState.Playing); - } - - break; - - case MultiplayerRoomState.Playing: - if (Room.Users.All(u => u.State != MultiplayerUserState.Playing)) - { - foreach (var u in Room.Users.Where(u => u.State == MultiplayerUserState.FinishedPlay)) - ChangeUserState(u.UserID, MultiplayerUserState.Results); - + // all users have bailed from the load sequence. cancel the game start. ChangeRoomState(MultiplayerRoomState.Open); - ((IMultiplayerClient)this).ResultsReady(); - - FinishCurrentItem().WaitSafely(); + return; } - break; - } - }); + foreach (var u in ServerRoom.Users.Where(u => u.State == MultiplayerUserState.Loaded)) + ChangeUserState(u.UserID, MultiplayerUserState.Playing); + + ((IMultiplayerClient)this).GameplayStarted(); + + ChangeRoomState(MultiplayerRoomState.Playing); + } + + break; + + case MultiplayerRoomState.Playing: + if (ServerRoom.Users.All(u => u.State != MultiplayerUserState.Playing)) + { + foreach (var u in ServerRoom.Users.Where(u => u.State == MultiplayerUserState.FinishedPlay)) + ChangeUserState(u.UserID, MultiplayerUserState.Results); + + ChangeRoomState(MultiplayerRoomState.Open); + ((IMultiplayerClient)this).ResultsReady(); + + FinishCurrentItem().WaitSafely(); + } + + break; + } } public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBeatmapAvailability) { - ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(newBeatmapAvailability)); + Debug.Assert(ServerRoom != null); + + var user = ServerRoom.Users.Single(u => u.UserID == userId); + user.BeatmapAvailability = clone(newBeatmapAvailability); + + ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(user.BeatmapAvailability)); } protected override async Task JoinRoom(long roomId, string? password = null) { - serverSideAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); + ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); - if (password != serverSideAPIRoom.Password.Value) + if (password != ServerAPIRoom.Password.Value) throw new InvalidOperationException("Invalid password."); - serverSidePlaylist.Clear(); - serverSidePlaylist.AddRange(serverSideAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item))); - lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID); + lastPlaylistItemId = ServerAPIRoom.Playlist.Max(item => item.ID); var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value }; - var room = new MultiplayerRoom(roomId) + ServerRoom = new MultiplayerRoom(roomId) { Settings = { - Name = serverSideAPIRoom.Name.Value, - MatchType = serverSideAPIRoom.Type.Value, + Name = ServerAPIRoom.Name.Value, + MatchType = ServerAPIRoom.Type.Value, Password = password, - QueueMode = serverSideAPIRoom.QueueMode.Value, - AutoStartDuration = serverSideAPIRoom.AutoStartDuration.Value + QueueMode = ServerAPIRoom.QueueMode.Value, + AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value }, - Playlist = serverSidePlaylist, + Playlist = ServerAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item)).ToList(), Users = { localUser }, Host = localUser }; - await updatePlaylistOrder(room).ConfigureAwait(false); - await updateCurrentItem(room, false).ConfigureAwait(false); + await updatePlaylistOrder(ServerRoom).ConfigureAwait(false); + await updateCurrentItem(ServerRoom, false).ConfigureAwait(false); - RoomSetupAction?.Invoke(room); + RoomSetupAction?.Invoke(ServerRoom); RoomSetupAction = null; - return clone(room); + return clone(ServerRoom); } protected override void OnRoomJoined() { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); // emulate the server sending this after the join room. scheduler required to make sure the join room event is fired first (in Join). - changeMatchType(Room.Settings.MatchType).WaitSafely(); + changeMatchType(ServerRoom.Settings.MatchType).WaitSafely(); RoomJoined = true; } @@ -235,28 +258,42 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(clone(userId)); + public override Task TransferHost(int userId) + { + Debug.Assert(ServerRoom != null); + + ServerRoom.Host = ServerRoom.Users.Single(u => u.UserID == userId); + + return ((IMultiplayerClient)this).HostChanged(clone(userId)); + } public override Task KickUser(int userId) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); - return ((IMultiplayerClient)this).UserKicked(clone(Room.Users.Single(u => u.UserID == userId))); + var user = ServerRoom.Users.Single(u => u.UserID == userId); + + ServerRoom.Users.Remove(user); + + return ((IMultiplayerClient)this).UserKicked(clone(user)); } public override async Task ChangeSettings(MultiplayerRoomSettings settings) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); + settings = clone(settings); + // Server is authoritative for the time being. - settings.PlaylistItemId = Room.Settings.PlaylistItemId; + settings.PlaylistItemId = ServerRoom.Settings.PlaylistItemId; + ServerRoom.Settings = settings; await changeQueueMode(settings.QueueMode).ConfigureAwait(false); await ((IMultiplayerClient)this).SettingsChanged(clone(settings)).ConfigureAwait(false); - foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) + foreach (var user in ServerRoom.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.Idle); await changeMatchType(settings.MatchType).ConfigureAwait(false); @@ -283,7 +320,12 @@ namespace osu.Game.Tests.Visual.Multiplayer public void ChangeUserMods(int userId, IEnumerable newMods) { - ((IMultiplayerClient)this).UserModsChanged(clone(userId), clone(newMods)); + Debug.Assert(ServerRoom != null); + + var user = ServerRoom.Users.Single(u => u.UserID == userId); + user.Mods = clone(newMods).ToArray(); + + ((IMultiplayerClient)this).UserModsChanged(clone(userId), clone(user.Mods)); } public override Task ChangeUserMods(IEnumerable newMods) @@ -294,14 +336,14 @@ namespace osu.Game.Tests.Visual.Multiplayer public override async Task SendMatchRequest(MatchUserRequest request) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(LocalUser != null); switch (request) { case ChangeTeamRequest changeTeam: - TeamVersusRoomState roomState = (TeamVersusRoomState)Room.MatchState!; + TeamVersusRoomState roomState = (TeamVersusRoomState)ServerRoom.MatchState!; TeamVersusUserState userState = (TeamVersusUserState)LocalUser.MatchState!; var targetTeam = roomState.Teams.FirstOrDefault(t => t.ID == changeTeam.TeamID); @@ -319,10 +361,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task StartMatch() { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); ChangeRoomState(MultiplayerRoomState.WaitingForLoad); - foreach (var user in Room.Users.Where(u => u.State == MultiplayerUserState.Ready)) + foreach (var user in ServerRoom.Users.Where(u => u.State == MultiplayerUserState.Ready)) ChangeUserState(user.UserID, MultiplayerUserState.WaitingForLoad); return ((IMultiplayerClient)this).LoadRequested(); @@ -339,35 +381,35 @@ namespace osu.Game.Tests.Visual.Multiplayer public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); - if (Room.Settings.QueueMode == QueueMode.HostOnly && Room.Host?.UserID != LocalUser?.UserID) + if (ServerRoom.Settings.QueueMode == QueueMode.HostOnly && ServerRoom.Host?.UserID != LocalUser?.UserID) throw new InvalidOperationException("Local user is not the room host."); item.OwnerID = userId; await addItem(item).ConfigureAwait(false); - await updateCurrentItem(Room).ConfigureAwait(false); + await updateCurrentItem(ServerRoom).ConfigureAwait(false); updateRoomStateIfRequired(); } - public override Task AddPlaylistItem(MultiplayerPlaylistItem item) => AddUserPlaylistItem(api.LocalUser.Value.OnlineID, item); + public override Task AddPlaylistItem(MultiplayerPlaylistItem item) => AddUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(item)); public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); - Debug.Assert(serverSideAPIRoom != null); + Debug.Assert(ServerAPIRoom != null); item.OwnerID = userId; - var existingItem = serverSidePlaylist.SingleOrDefault(i => i.ID == item.ID); + var existingItem = ServerRoom.Playlist.SingleOrDefault(i => i.ID == item.ID); if (existingItem == null) throw new InvalidOperationException("Attempted to change an item that doesn't exist."); - if (existingItem.OwnerID != userId && Room.Host?.UserID != LocalUser?.UserID) + if (existingItem.OwnerID != userId && ServerRoom.Host?.UserID != LocalUser?.UserID) throw new InvalidOperationException("Attempted to change an item which is not owned by the user."); if (existingItem.Expired) @@ -376,20 +418,20 @@ namespace osu.Game.Tests.Visual.Multiplayer // Ensure the playlist order doesn't change. item.PlaylistOrder = existingItem.PlaylistOrder; - serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item; - serverSideAPIRoom.Playlist[serverSideAPIRoom.Playlist.IndexOf(serverSideAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); + ServerRoom.Playlist[ServerRoom.Playlist.IndexOf(existingItem)] = item; + ServerAPIRoom.Playlist[ServerAPIRoom.Playlist.IndexOf(ServerAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); await ((IMultiplayerClient)this).PlaylistItemChanged(clone(item)).ConfigureAwait(false); } - public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, item); + public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(item)); public async Task RemoveUserPlaylistItem(int userId, long playlistItemId) { - Debug.Assert(Room != null); - Debug.Assert(serverSideAPIRoom != null); + Debug.Assert(ServerRoom != null); + Debug.Assert(ServerAPIRoom != null); - var item = serverSidePlaylist.Find(i => i.ID == playlistItemId); + var item = ServerRoom.Playlist.FirstOrDefault(i => i.ID == playlistItemId); if (item == null) throw new InvalidOperationException("Item does not exist in the room."); @@ -403,11 +445,11 @@ namespace osu.Game.Tests.Visual.Multiplayer if (item.Expired) throw new InvalidOperationException("Attempted to remove an item which has already been played."); - serverSidePlaylist.Remove(item); - serverSideAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); + ServerRoom.Playlist.Remove(item); + ServerAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); await ((IMultiplayerClient)this).PlaylistItemRemoved(clone(playlistItemId)).ConfigureAwait(false); - await updateCurrentItem(Room).ConfigureAwait(false); + await updateCurrentItem(ServerRoom).ConfigureAwait(false); updateRoomStateIfRequired(); } @@ -415,42 +457,52 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task changeMatchType(MatchType type) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); switch (type) { case MatchType.HeadToHead: - await ((IMultiplayerClient)this).MatchRoomStateChanged(null).ConfigureAwait(false); + ServerRoom.MatchState = null; + await ((IMultiplayerClient)this).MatchRoomStateChanged(clone(ServerRoom.MatchState)).ConfigureAwait(false); + + foreach (var user in ServerRoom.Users) + { + user.MatchState = null; + await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(user.MatchState)).ConfigureAwait(false); + } - foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), null).ConfigureAwait(false); break; case MatchType.TeamVersus: - await ((IMultiplayerClient)this).MatchRoomStateChanged(clone(TeamVersusRoomState.CreateDefault())).ConfigureAwait(false); + ServerRoom.MatchState = TeamVersusRoomState.CreateDefault(); + await ((IMultiplayerClient)this).MatchRoomStateChanged(clone(ServerRoom.MatchState)).ConfigureAwait(false); + + foreach (var user in ServerRoom.Users) + { + user.MatchState = new TeamVersusUserState(); + await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(user.MatchState)).ConfigureAwait(false); + } - foreach (var user in Room.Users) - await ((IMultiplayerClient)this).MatchUserStateChanged(clone(user.UserID), clone(new TeamVersusUserState())).ConfigureAwait(false); break; } } private async Task changeQueueMode(QueueMode newMode) { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); // When changing to host-only mode, ensure that at least one non-expired playlist item exists by duplicating the current item. - if (newMode == QueueMode.HostOnly && serverSidePlaylist.All(item => item.Expired)) + if (newMode == QueueMode.HostOnly && ServerRoom.Playlist.All(item => item.Expired)) await duplicateCurrentItem().ConfigureAwait(false); - await updatePlaylistOrder(Room).ConfigureAwait(false); - await updateCurrentItem(Room).ConfigureAwait(false); + await updatePlaylistOrder(ServerRoom).ConfigureAwait(false); + await updateCurrentItem(ServerRoom).ConfigureAwait(false); } public async Task FinishCurrentItem() { - Debug.Assert(Room != null); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); // Expire the current playlist item. @@ -458,13 +510,13 @@ namespace osu.Game.Tests.Visual.Multiplayer currentItem.PlayedAt = DateTimeOffset.Now; await ((IMultiplayerClient)this).PlaylistItemChanged(clone(currentItem)).ConfigureAwait(false); - await updatePlaylistOrder(Room).ConfigureAwait(false); + await updatePlaylistOrder(ServerRoom).ConfigureAwait(false); // In host-only mode, a duplicate playlist item will be used for the next round. - if (Room.Settings.QueueMode == QueueMode.HostOnly && serverSidePlaylist.All(item => item.Expired)) + if (ServerRoom.Settings.QueueMode == QueueMode.HostOnly && ServerRoom.Playlist.All(item => item.Expired)) await duplicateCurrentItem().ConfigureAwait(false); - await updateCurrentItem(Room).ConfigureAwait(false); + await updateCurrentItem(ServerRoom).ConfigureAwait(false); } private async Task duplicateCurrentItem() @@ -483,26 +535,28 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task addItem(MultiplayerPlaylistItem item) { - Debug.Assert(Room != null); - Debug.Assert(serverSideAPIRoom != null); + Debug.Assert(ServerRoom != null); + Debug.Assert(ServerAPIRoom != null); item.ID = ++lastPlaylistItemId; - serverSidePlaylist.Add(item); - serverSideAPIRoom.Playlist.Add(new PlaylistItem(item)); + ServerRoom.Playlist.Add(item); + ServerAPIRoom.Playlist.Add(new PlaylistItem(item)); await ((IMultiplayerClient)this).PlaylistItemAdded(clone(item)).ConfigureAwait(false); - await updatePlaylistOrder(Room).ConfigureAwait(false); + await updatePlaylistOrder(ServerRoom).ConfigureAwait(false); } - private IEnumerable upcomingItems => serverSidePlaylist.Where(i => !i.Expired).OrderBy(i => i.PlaylistOrder); + private IEnumerable upcomingItems => ServerRoom?.Playlist.Where(i => !i.Expired).OrderBy(i => i.PlaylistOrder) ?? Enumerable.Empty(); private async Task updateCurrentItem(MultiplayerRoom room, bool notify = true) { - // Pick the next non-expired playlist item by playlist order, or default to the most-recently-expired item. - MultiplayerPlaylistItem nextItem = upcomingItems.FirstOrDefault() ?? serverSidePlaylist.OrderByDescending(i => i.PlayedAt).First(); + Debug.Assert(ServerRoom != null); - currentIndex = serverSidePlaylist.IndexOf(nextItem); + // Pick the next non-expired playlist item by playlist order, or default to the most-recently-expired item. + MultiplayerPlaylistItem nextItem = upcomingItems.FirstOrDefault() ?? ServerRoom.Playlist.OrderByDescending(i => i.PlayedAt).First(); + + currentIndex = ServerRoom.Playlist.IndexOf(nextItem); long lastItem = room.Settings.PlaylistItemId; room.Settings.PlaylistItemId = nextItem.ID; @@ -513,21 +567,22 @@ namespace osu.Game.Tests.Visual.Multiplayer private async Task updatePlaylistOrder(MultiplayerRoom room) { - Debug.Assert(serverSideAPIRoom != null); + Debug.Assert(ServerRoom != null); + Debug.Assert(ServerAPIRoom != null); List orderedActiveItems; switch (room.Settings.QueueMode) { default: - orderedActiveItems = serverSidePlaylist.Where(item => !item.Expired).OrderBy(item => item.ID).ToList(); + orderedActiveItems = ServerRoom.Playlist.Where(item => !item.Expired).OrderBy(item => item.ID).ToList(); break; case QueueMode.AllPlayersRoundRobin: var itemsByPriority = new List<(MultiplayerPlaylistItem item, int priority)>(); // Assign a priority for items from each user, starting from 0 and increasing in order which the user added the items. - foreach (var group in serverSidePlaylist.Where(item => !item.Expired).OrderBy(item => item.ID).GroupBy(item => item.OwnerID)) + foreach (var group in ServerRoom.Playlist.Where(item => !item.Expired).OrderBy(item => item.ID).GroupBy(item => item.OwnerID)) { int priority = 0; itemsByPriority.AddRange(group.Select(item => (item, priority++))); @@ -562,8 +617,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } // Also ensure that the API room's playlist is correct. - foreach (var item in serverSideAPIRoom.Playlist) - item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder; + foreach (var item in ServerAPIRoom.Playlist) + item.PlaylistOrder = ServerRoom.Playlist.Single(i => i.ID == item.ID).PlaylistOrder; } private T clone(T incoming) From 37acec1952f58552d3c0d1b721a12561fc71ef7c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 19:15:31 +0900 Subject: [PATCH 5/8] Isolate client's LocalUser from TestMultiplayerClient --- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 3165a4ecff..7c14851176 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -56,6 +56,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public new MultiplayerRoom Room => throw new InvalidOperationException($"Accessing the client-side room via {nameof(TestMultiplayerClient)} is unsafe. " + $"Use {nameof(ClientRoom)} if this was intended."); + public new MultiplayerRoomUser? LocalUser => ServerRoom?.Users.SingleOrDefault(u => u.User?.Id == API.LocalUser.Value.Id); + public Action? RoomSetupAction; public bool RoomJoined { get; private set; } From 084a65578cc532482a2377754b72129e090b7deb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 19:36:37 +0900 Subject: [PATCH 6/8] Always clone messages incoming from client-side --- .../Multiplayer/TestMultiplayerClient.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 7c14851176..19b887eea5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -201,13 +201,16 @@ namespace osu.Game.Tests.Visual.Multiplayer Debug.Assert(ServerRoom != null); var user = ServerRoom.Users.Single(u => u.UserID == userId); - user.BeatmapAvailability = clone(newBeatmapAvailability); + user.BeatmapAvailability = newBeatmapAvailability; ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(user.BeatmapAvailability)); } protected override async Task JoinRoom(long roomId, string? password = null) { + roomId = clone(roomId); + password = clone(password); + ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); if (password != ServerAPIRoom.Password.Value) @@ -262,6 +265,8 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task TransferHost(int userId) { + userId = clone(userId); + Debug.Assert(ServerRoom != null); ServerRoom.Host = ServerRoom.Users.Single(u => u.UserID == userId); @@ -271,10 +276,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task KickUser(int userId) { + userId = clone(userId); + Debug.Assert(ServerRoom != null); var user = ServerRoom.Users.Single(u => u.UserID == userId); - ServerRoom.Users.Remove(user); return ((IMultiplayerClient)this).UserKicked(clone(user)); @@ -282,11 +288,11 @@ namespace osu.Game.Tests.Visual.Multiplayer public override async Task ChangeSettings(MultiplayerRoomSettings settings) { + settings = clone(settings); + Debug.Assert(ServerRoom != null); Debug.Assert(currentItem != null); - settings = clone(settings); - // Server is authoritative for the time being. settings.PlaylistItemId = ServerRoom.Settings.PlaylistItemId; ServerRoom.Settings = settings; @@ -304,40 +310,44 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task ChangeState(MultiplayerUserState newState) { + newState = clone(newState); + if (newState == MultiplayerUserState.Idle && LocalUser?.State == MultiplayerUserState.WaitingForLoad) return Task.CompletedTask; - ChangeUserState(api.LocalUser.Value.Id, newState); + ChangeUserState(api.LocalUser.Value.Id, clone(newState)); return Task.CompletedTask; } public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) { - ChangeUserBeatmapAvailability(api.LocalUser.Value.Id, newBeatmapAvailability); + ChangeUserBeatmapAvailability(api.LocalUser.Value.Id, clone(newBeatmapAvailability)); return Task.CompletedTask; } public void ChangeUserMods(int userId, IEnumerable newMods) - => ChangeUserMods(userId, newMods.Select(m => new APIMod(m)).ToList()); + => ChangeUserMods(userId, newMods.Select(m => new APIMod(m))); public void ChangeUserMods(int userId, IEnumerable newMods) { Debug.Assert(ServerRoom != null); var user = ServerRoom.Users.Single(u => u.UserID == userId); - user.Mods = clone(newMods).ToArray(); + user.Mods = newMods.ToArray(); ((IMultiplayerClient)this).UserModsChanged(clone(userId), clone(user.Mods)); } public override Task ChangeUserMods(IEnumerable newMods) { - ChangeUserMods(api.LocalUser.Value.Id, newMods); + ChangeUserMods(api.LocalUser.Value.Id, clone(newMods)); return Task.CompletedTask; } public override async Task SendMatchRequest(MatchUserRequest request) { + request = clone(request); + Debug.Assert(ServerRoom != null); Debug.Assert(LocalUser != null); @@ -455,7 +465,7 @@ namespace osu.Game.Tests.Visual.Multiplayer updateRoomStateIfRequired(); } - public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, playlistItemId); + public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(playlistItemId)); private async Task changeMatchType(MatchType type) { From 93c68e4329fca6158d62984a0c714eb157f825f5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 19:46:46 +0900 Subject: [PATCH 7/8] More test hardening around ClientAPIRoom usages --- .../Visual/Multiplayer/QueueModeTestScene.cs | 2 +- .../Multiplayer/TestSceneAllPlayersQueueMode.cs | 16 ++++++++-------- .../Multiplayer/TestSceneHostOnlyQueueMode.cs | 14 +++++++------- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 10 +++++----- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Multiplayer/TestSceneMultiplayerPlayer.cs | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 53bed75723..2b461cf6f6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestCreatedWithCorrectMode() { - AddAssert("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode); + AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode); } protected void RunGameplay() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs index a94a60f12d..5947cabf7f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneAllPlayersQueueMode.cs @@ -37,10 +37,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestItemAddedToTheEndOfQueue() { addItem(() => OtherBeatmap); - AddAssert("playlist has 2 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); + AddUntilStep("playlist has 2 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); addItem(() => InitialBeatmap); - AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); + AddUntilStep("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); AddUntilStep("first item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } @@ -50,8 +50,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { RunGameplay(); - AddAssert("playlist has only one item", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 1); - AddAssert("playlist item is expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddUntilStep("playlist has only one item", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 1); + AddUntilStep("playlist item is expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); AddUntilStep("last item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID); } @@ -63,12 +63,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); - AddAssert("first item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddUntilStep("first item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); RunGameplay(); - AddAssert("second item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == true); + AddUntilStep("second item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == true); AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[2].ID); } @@ -82,8 +82,8 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(queueMode: QueueMode.HostOnly)); - AddAssert("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); - AddAssert("item 2 is not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); + AddUntilStep("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3); + AddUntilStep("item 2 is not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); AddUntilStep("current item is the other beatmap", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == 2); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index 15a1354c98..800b523a9d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -48,9 +48,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { RunGameplay(); - AddAssert("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); - AddAssert("first playlist item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); - AddAssert("second playlist item not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); + AddUntilStep("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); + AddUntilStep("first playlist item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true); + AddUntilStep("second playlist item not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false); AddUntilStep("second playlist item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID); } @@ -60,12 +60,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); IBeatmapInfo firstBeatmap = null; - AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ClientAPIRoom?.Playlist[0].Beatmap); + AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap); selectNewItem(() => OtherBeatmap); - AddAssert("first playlist item hasn't changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Beatmap == firstBeatmap); - AddAssert("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap); + AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap == firstBeatmap); + AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap); } [Test] @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { addItem(() => OtherBeatmap); - AddAssert("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); + AddUntilStep("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2); } private void selectNewItem(Func beatmap) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 193d1d4248..da48fb7332 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -243,8 +243,8 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddAssert("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); - AddAssert("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] @@ -303,8 +303,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); - AddAssert("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); - AddAssert("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] @@ -323,7 +323,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddAssert("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password"); + AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password"); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index f9eec6b728..aa935cb1d4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -254,7 +254,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("last playlist item selected", () => { - var lastItem = this.ChildrenOfType().Single(p => p.Item.ID == MultiplayerClient.ClientAPIRoom?.Playlist.Last().ID); + var lastItem = this.ChildrenOfType().Single(p => p.Item.ID == MultiplayerClient.ServerAPIRoom?.Playlist.Last().ID); return lastItem.IsSelectedItem; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index 9fee771b97..f6a6b3c667 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("initialise gameplay", () => { - Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ClientAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) + Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) { RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID, }, MultiplayerClient.ServerRoom?.Users.ToArray())); From 6644f9a72a4fd7d9dbf96d00572eb1e57a7019bf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Jul 2022 21:14:05 +0900 Subject: [PATCH 8/8] Fix completely incorrect test --- .../NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 0bcb38327e..d1c5e2d8b3 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("add user", () => MultiplayerClient.AddUser(user)); AddUntilStep("room has 2 users", () => MultiplayerClient.ClientRoom?.Users.Count == 2); - AddRepeatStep("remove user multiple times", () => MultiplayerClient.RemoveUser(user), 3); + AddStep("remove user", () => MultiplayerClient.RemoveUser(user)); AddUntilStep("room has 1 user", () => MultiplayerClient.ClientRoom?.Users.Count == 1); }