diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 636cba719b..1f85aa5d45 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -170,13 +170,23 @@ namespace osu.Game.Online.Multiplayer private readonly TaskChain joinOrLeaveTaskChain = new TaskChain(); private CancellationTokenSource? joinCancellationSource; + /// + /// Creates and joins a described by an API . + /// + /// The API describing the room to create. + /// If the current user is already in another room. public async Task CreateRoom(Room room) { if (Room != null) throw new InvalidOperationException("Cannot create a multiplayer room while already in one."); var cancellationSource = joinCancellationSource = new CancellationTokenSource(); - await initRoom(room, r => CreateRoomInternal(new MultiplayerRoom(room)), cancellationSource.Token).ConfigureAwait(false); + + await joinOrLeaveTaskChain.Add(async () => + { + var multiplayerRoom = await CreateRoomInternal(new MultiplayerRoom(room)).ConfigureAwait(false); + await setupJoinedRoom(room, multiplayerRoom, cancellationSource.Token).ConfigureAwait(false); + }, cancellationSource.Token).ConfigureAwait(false); } /// @@ -184,54 +194,61 @@ namespace osu.Game.Online.Multiplayer /// /// The API . /// An optional password to use for the join operation. + /// If the current user is already in another room, or does not represent an active room. public async Task JoinRoom(Room room, string? password = null) { if (Room != null) throw new InvalidOperationException("Cannot join a multiplayer room while already in one."); - Debug.Assert(room.RoomID != null); + if (room.RoomID == null) + throw new InvalidOperationException("Cannot join an inactive room."); var cancellationSource = joinCancellationSource = new CancellationTokenSource(); - await initRoom(room, r => JoinRoomInternal(room.RoomID.Value, password ?? room.Password), cancellationSource.Token).ConfigureAwait(false); - } - private async Task initRoom(Room room, Func> initFunc, CancellationToken cancellationToken) - { await joinOrLeaveTaskChain.Add(async () => { - // Initialise the server-side room. - MultiplayerRoom joinedRoom = await initFunc(room).ConfigureAwait(false); + var multiplayerRoom = await JoinRoomInternal(room.RoomID.Value, password ?? room.Password).ConfigureAwait(false); + await setupJoinedRoom(room, multiplayerRoom, cancellationSource.Token).ConfigureAwait(false); + }, cancellationSource.Token).ConfigureAwait(false); + } - // Populate users. - await PopulateUsers(joinedRoom.Users).ConfigureAwait(false); + /// + /// Performs post-join setup of a . + /// + /// The incoming API that was requested to be joined. + /// The resuling that was joined. + /// A token to cancel the process. + private async Task setupJoinedRoom(Room apiRoom, MultiplayerRoom joinedRoom, CancellationToken cancellationToken) + { + // Populate users. + await PopulateUsers(joinedRoom.Users).ConfigureAwait(false); - // Update the stored room (must be done on update thread for thread-safety). - await runOnUpdateThreadAsync(() => - { - Debug.Assert(Room == null); - Debug.Assert(APIRoom == null); + // Update the stored room (must be done on update thread for thread-safety). + await runOnUpdateThreadAsync(() => + { + Debug.Assert(Room == null); + Debug.Assert(APIRoom == null); - Room = joinedRoom; - APIRoom = room; + Room = joinedRoom; + APIRoom = apiRoom; - APIRoom.RoomID = joinedRoom.RoomID; - APIRoom.Playlist = joinedRoom.Playlist.Select(item => new PlaylistItem(item)).ToArray(); - APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId); - // The server will null out the end date upon the host joining the room, but the null value is never communicated to the client. - APIRoom.EndDate = null; + APIRoom.RoomID = joinedRoom.RoomID; + APIRoom.Playlist = joinedRoom.Playlist.Select(item => new PlaylistItem(item)).ToArray(); + APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId); + // The server will null out the end date upon the host joining the room, but the null value is never communicated to the client. + APIRoom.EndDate = null; - Debug.Assert(LocalUser != null); - addUserToAPIRoom(LocalUser); + Debug.Assert(LocalUser != null); + addUserToAPIRoom(LocalUser); - foreach (var user in joinedRoom.Users) - updateUserPlayingState(user.UserID, user.State); + foreach (var user in joinedRoom.Users) + updateUserPlayingState(user.UserID, user.State); - updateLocalRoomSettings(joinedRoom.Settings); + updateLocalRoomSettings(joinedRoom.Settings); - postServerShuttingDownNotification(); + postServerShuttingDownNotification(); - OnRoomJoined(); - }, cancellationToken).ConfigureAwait(false); + OnRoomJoined(); }, cancellationToken).ConfigureAwait(false); }