From ad422295c85d257044edd33dba7284b7c8d9b631 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 10 Jan 2025 21:38:37 +0900
Subject: [PATCH 01/13] Add ctor to create Rooms from MultiplayerRooms
---
osu.Game/Online/Rooms/Room.cs | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index f8660a656e6f..764713464644 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,6 +342,29 @@ public RoomAvailability Availability
// Not yet serialised (not implemented).
private RoomAvailability availability;
+ public Room()
+ {
+ }
+
+ ///
+ /// Creates a from a .
+ ///
+ public Room(MultiplayerRoom room)
+ {
+ RoomID = room.RoomID;
+ Host = room.Host?.User;
+
+ Name = room.Settings.Name;
+ Password = room.Settings.Password;
+ Type = room.Settings.MatchType;
+ QueueMode = room.Settings.QueueMode;
+ AutoStartDuration = room.Settings.AutoStartDuration;
+ AutoSkip = room.Settings.AutoSkip;
+
+ Playlist = room.Playlist.Select(item => new PlaylistItem(item)).ToArray();
+ CurrentPlaylistItem = Playlist.FirstOrDefault(item => item.ID == room.Settings.PlaylistItemId);
+ }
+
///
/// Copies values from another into this one.
///
From 3d2d4ee89f06a88feabcfdda1b73ac1cbeaf1c49 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 10 Jan 2025 22:07:13 +0900
Subject: [PATCH 02/13] Add ctor to create MultiplayerPlaylistItem from
PlaylistItem
---
osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
index 8be703e620be..6e467c1d2652 100644
--- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
+++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
@@ -60,5 +60,20 @@ public class MultiplayerPlaylistItem
public MultiplayerPlaylistItem()
{
}
+
+ public MultiplayerPlaylistItem(PlaylistItem item)
+ {
+ ID = item.ID;
+ OwnerID = item.OwnerID;
+ BeatmapID = item.Beatmap.OnlineID;
+ BeatmapChecksum = item.Beatmap.MD5Hash;
+ RulesetID = item.RulesetID;
+ RequiredMods = item.RequiredMods.ToArray();
+ AllowedMods = item.AllowedMods.ToArray();
+ Expired = item.Expired;
+ PlaylistOrder = item.PlaylistOrder ?? 0;
+ PlayedAt = item.PlayedAt;
+ StarRating = item.Beatmap.StarRating;
+ }
}
}
From ad28de8ae3aa1d2817fc8511929d52c2c3ab0b20 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 17 Jan 2025 21:44:40 +0900
Subject: [PATCH 03/13] Create multiplayer rooms via multiplayer server
---
.../Multiplayer/IMultiplayerLoungeServer.cs | 2 +
.../Online/Multiplayer/MultiplayerClient.cs | 40 +++++++++++-----
.../Online/Multiplayer/MultiplayerRoom.cs | 9 ++++
.../Multiplayer/MultiplayerRoomSettings.cs | 14 ++++++
.../Multiplayer/OnlineMultiplayerClient.cs | 25 ++++++++++
osu.Game/Online/Rooms/Room.cs | 23 ----------
.../Match/MultiplayerMatchSettingsOverlay.cs | 46 +++++++++----------
.../Multiplayer/MultiplayerMatchSubScreen.cs | 5 +-
.../Multiplayer/TestMultiplayerClient.cs | 5 ++
9 files changed, 105 insertions(+), 64 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
index f266c38b8b23..c5eb6f9b36c6 100644
--- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
+++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
@@ -10,6 +10,8 @@ namespace osu.Game.Online.Multiplayer
///
public interface IMultiplayerLoungeServer
{
+ Task CreateRoom(MultiplayerRoom room);
+
///
/// Request to join a multiplayer room.
///
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index 4a28124583bb..d0c3a1fa0687 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -165,6 +165,15 @@ private void load()
private readonly TaskChain joinOrLeaveTaskChain = new TaskChain();
private CancellationTokenSource? joinCancellationSource;
+ public async Task CreateRoom(Room room)
+ {
+ if (Room != null)
+ throw new InvalidOperationException("Cannot join a multiplayer room while already in one.");
+
+ var cancellationSource = joinCancellationSource = new CancellationTokenSource();
+ await initRoom(room, r => CreateRoom(new MultiplayerRoom(room)), cancellationSource.Token);
+ }
+
///
/// Joins the for a given API .
///
@@ -175,34 +184,34 @@ 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);
+
var cancellationSource = joinCancellationSource = new CancellationTokenSource();
+ await initRoom(room, r => JoinRoom(room.RoomID.Value, password ?? room.Password), cancellationSource.Token);
+ }
+ private async Task initRoom(Room room, Func> initFunc, CancellationToken cancellationToken)
+ {
await joinOrLeaveTaskChain.Add(async () =>
{
- Debug.Assert(room.RoomID != null);
-
- // Join the server-side room.
- var joinedRoom = await JoinRoom(room.RoomID.Value, password ?? room.Password).ConfigureAwait(false);
- Debug.Assert(joinedRoom != null);
+ // Initialise the server-side room.
+ MultiplayerRoom joinedRoom = await initFunc(room).ConfigureAwait(false);
// Populate users.
- Debug.Assert(joinedRoom.Users != null);
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);
Room = joinedRoom;
APIRoom = room;
- Debug.Assert(joinedRoom.Playlist.Count > 0);
-
+ 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);
@@ -216,8 +225,8 @@ await runOnUpdateThreadAsync(() =>
postServerShuttingDownNotification();
OnRoomJoined();
- }, cancellationSource.Token).ConfigureAwait(false);
- }, cancellationSource.Token).ConfigureAwait(false);
+ }, cancellationToken).ConfigureAwait(false);
+ }, cancellationToken).ConfigureAwait(false);
}
///
@@ -227,6 +236,13 @@ protected virtual void OnRoomJoined()
{
}
+ ///
+ /// Creates the with the given settings.
+ ///
+ /// The room.
+ /// The joined
+ protected abstract Task CreateRoom(MultiplayerRoom room);
+
///
/// Joins the with a given ID.
///
diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs
index 00048fa93197..f7bd4490ff6a 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using MessagePack;
using Newtonsoft.Json;
using osu.Game.Online.Rooms;
@@ -65,6 +66,14 @@ public MultiplayerRoom(long roomId)
RoomID = roomId;
}
+ public MultiplayerRoom(Room room)
+ {
+ RoomID = room.RoomID ?? 0;
+ Settings = new MultiplayerRoomSettings(room);
+ Host = room.Host != null ? new MultiplayerRoomUser(room.Host.OnlineID) : null;
+ Playlist = room.Playlist.Select(p => new MultiplayerPlaylistItem(p)).ToArray();
+ }
+
public override string ToString() => $"RoomID:{RoomID} Host:{Host?.UserID} Users:{Users.Count} State:{State} Settings: [{Settings}]";
}
}
diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
index c73b02874ebd..c264ec1eefb6 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
@@ -35,6 +35,20 @@ public class MultiplayerRoomSettings : IEquatable
[IgnoreMember]
public bool AutoStartEnabled => AutoStartDuration != TimeSpan.Zero;
+ public MultiplayerRoomSettings()
+ {
+ }
+
+ public MultiplayerRoomSettings(Room room)
+ {
+ Name = room.Name;
+ Password = room.Password ?? string.Empty;
+ MatchType = room.Type;
+ QueueMode = room.QueueMode;
+ AutoStartDuration = room.AutoStartDuration;
+ AutoSkip = room.AutoSkip;
+ }
+
public bool Equals(MultiplayerRoomSettings? other)
{
if (ReferenceEquals(this, other)) return true;
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 40436d730e3c..524873ef663b 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -266,6 +266,31 @@ public override Task RemovePlaylistItem(long playlistItemId)
return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId);
}
+ protected override async Task CreateRoom(MultiplayerRoom room)
+ {
+ if (!IsConnected.Value)
+ throw new OperationCanceledException();
+
+ Debug.Assert(connection != null);
+
+ try
+ {
+ return await connection.InvokeAsync(nameof(IMultiplayerServer.CreateRoom), room);
+ }
+ catch (HubException exception)
+ {
+ if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
+ {
+ Debug.Assert(connector != null);
+
+ await connector.Reconnect().ConfigureAwait(false);
+ return await CreateRoom(room).ConfigureAwait(false);
+ }
+
+ throw;
+ }
+ }
+
public override Task DisconnectInternal()
{
if (connector == null)
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index 764713464644..f8660a656e6f 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,29 +342,6 @@ public RoomAvailability Availability
// Not yet serialised (not implemented).
private RoomAvailability availability;
- public Room()
- {
- }
-
- ///
- /// Creates a from a .
- ///
- public Room(MultiplayerRoom room)
- {
- RoomID = room.RoomID;
- Host = room.Host?.User;
-
- Name = room.Settings.Name;
- Password = room.Settings.Password;
- Type = room.Settings.MatchType;
- QueueMode = room.Settings.QueueMode;
- AutoStartDuration = room.Settings.AutoStartDuration;
- AutoSkip = room.Settings.AutoSkip;
-
- Playlist = room.Playlist.Select(item => new PlaylistItem(item)).ToArray();
- CurrentPlaylistItem = Playlist.FirstOrDefault(item => item.ID == room.Settings.PlaylistItemId);
- }
-
///
/// Copies values from another into this one.
///
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 137205414901..279b140d36c0 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -29,12 +29,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public partial class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay
{
- public required Bindable SelectedItem
- {
- get => selectedItem;
- set => selectedItem.Current = value;
- }
-
protected override OsuButton SubmitButton => settings.ApplyButton;
protected override bool IsLoading => ongoingOperationTracker.InProgress.Value;
@@ -56,7 +50,6 @@ public MultiplayerMatchSettingsOverlay(Room room)
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
SettingsApplied = Hide,
- SelectedItem = { BindTarget = SelectedItem }
};
protected partial class MatchSettings : CompositeDrawable
@@ -65,7 +58,6 @@ protected partial class MatchSettings : CompositeDrawable
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
- public readonly Bindable SelectedItem = new Bindable();
public Action? SettingsApplied;
public OsuTextBox NameField = null!;
@@ -86,9 +78,6 @@ protected partial class MatchSettings : CompositeDrawable
[Resolved]
private MultiplayerMatchSubScreen matchSubScreen { get; set; } = null!;
- [Resolved]
- private IRoomManager manager { get; set; } = null!;
-
[Resolved]
private MultiplayerClient client { get; set; } = null!;
@@ -279,7 +268,6 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
RelativeSizeAxes = Axes.X,
Height = DrawableRoomPlaylistItem.HEIGHT,
- SelectedItem = { BindTarget = SelectedItem }
},
selectBeatmapButton = new RoundedButton
{
@@ -482,19 +470,27 @@ private void apply()
}
else
{
- room.Name = NameField.Text;
- room.Type = TypePicker.Current.Value;
- room.Password = PasswordTextBox.Current.Value;
- room.QueueMode = QueueModeDropdown.Current.Value;
- room.AutoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value);
- room.AutoSkip = AutoSkipCheckbox.Current.Value;
-
- if (int.TryParse(MaxParticipantsField.Text, out int max))
- room.MaxParticipants = max;
- else
- room.MaxParticipants = null;
+ client.CreateRoom(room).ContinueWith(t => Schedule(() =>
+ {
+ if (t.IsCompleted)
+ onSuccess(room);
+ else if (t.IsFaulted)
+ {
+ Exception? exception = t.Exception;
+
+ if (exception is AggregateException ae)
+ exception = ae.InnerException;
- manager.CreateRoom(room, onSuccess, onError);
+ Debug.Assert(exception != null);
+
+ if (exception.GetHubExceptionMessage() is string message)
+ onError(message);
+ else
+ onError($"Error creating room: {exception}");
+ }
+ else
+ onError("Error creating room.");
+ }));
}
}
@@ -520,7 +516,7 @@ private void onError(string text) => Schedule(() =>
if (text.StartsWith(not_found_prefix, StringComparison.Ordinal))
{
ErrorText.Text = "The selected beatmap is not available online.";
- SelectedItem.Value?.MarkInvalid();
+ room.Playlist.SingleOrDefault()?.MarkInvalid();
}
else
{
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index edc45dbf7c49..06ea5ee033d1 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -233,10 +233,7 @@ internal void OpenSongSelection(PlaylistItem? itemToEdit = null)
SelectedItem = SelectedItem
};
- protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room)
- {
- SelectedItem = SelectedItem
- };
+ protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room);
protected override void UpdateMods()
{
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index 4d812abf11f0..70e298f3e045 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -483,6 +483,11 @@ public async Task RemoveUserPlaylistItem(int userId, long playlistItemId)
public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(playlistItemId));
+ protected override Task CreateRoom(MultiplayerRoom room)
+ {
+ throw new NotImplementedException();
+ }
+
private async Task changeMatchType(MatchType type)
{
Debug.Assert(ServerRoom != null);
From 001d9cacf21cbe9dee9330b01b9e496e7be1f4f5 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 21 Jan 2025 19:31:49 +0900
Subject: [PATCH 04/13] Configure awaiters
---
osu.Game/Online/Multiplayer/MultiplayerClient.cs | 4 ++--
osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index d0c3a1fa0687..e5eade8c1da2 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -171,7 +171,7 @@ public async Task CreateRoom(Room room)
throw new InvalidOperationException("Cannot join a multiplayer room while already in one.");
var cancellationSource = joinCancellationSource = new CancellationTokenSource();
- await initRoom(room, r => CreateRoom(new MultiplayerRoom(room)), cancellationSource.Token);
+ await initRoom(room, r => CreateRoom(new MultiplayerRoom(room)), cancellationSource.Token).ConfigureAwait(false);
}
///
@@ -187,7 +187,7 @@ public async Task JoinRoom(Room room, string? password = null)
Debug.Assert(room.RoomID != null);
var cancellationSource = joinCancellationSource = new CancellationTokenSource();
- await initRoom(room, r => JoinRoom(room.RoomID.Value, password ?? room.Password), cancellationSource.Token);
+ await initRoom(room, r => JoinRoom(room.RoomID.Value, password ?? room.Password), cancellationSource.Token).ConfigureAwait(false);
}
private async Task initRoom(Room room, Func> initFunc, CancellationToken cancellationToken)
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 524873ef663b..05f3e444054b 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -275,7 +275,7 @@ protected override async Task CreateRoom(MultiplayerRoom room)
try
{
- return await connection.InvokeAsync(nameof(IMultiplayerServer.CreateRoom), room);
+ return await connection.InvokeAsync(nameof(IMultiplayerServer.CreateRoom), room).ConfigureAwait(false);
}
catch (HubException exception)
{
From 02369baec43f0a68a26a960bef20980289b1f6ab Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 22 Jan 2025 21:44:45 +0900
Subject: [PATCH 05/13] Join/Leave rooms via multiplayer server
Relevant functionality has been removed from `RoomManager` in the
process.
---
.../TestSceneMultiplayerLoungeSubScreen.cs | 26 ------
.../Online/Multiplayer/MultiplayerClient.cs | 3 +
osu.Game/Online/Rooms/CreateRoomRequest.cs | 2 +-
osu.Game/Online/Rooms/JoinRoomRequest.cs | 1 +
.../OnlinePlay/Components/RoomManager.cs | 80 -------------------
.../DailyChallenge/DailyChallenge.cs | 10 +--
osu.Game/Screens/OnlinePlay/IRoomManager.cs | 22 -----
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 9 ++-
.../Screens/OnlinePlay/Match/RoomSubScreen.cs | 6 +-
.../OnlinePlay/Multiplayer/Multiplayer.cs | 3 -
.../Multiplayer/MultiplayerLoungeSubScreen.cs | 34 ++++----
.../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +
.../Multiplayer/MultiplayerRoomManager.cs | 72 -----------------
.../Screens/OnlinePlay/OnlinePlayScreen.cs | 12 +--
.../Screens/OnlinePlay/OnlinePlaySubScreen.cs | 4 -
.../Playlists/PlaylistsLoungeSubScreen.cs | 15 ++++
.../Playlists/PlaylistsRoomSettingsOverlay.cs | 9 ++-
.../Playlists/PlaylistsRoomSubScreen.cs | 2 +
.../Multiplayer/MultiplayerTestScene.cs | 2 +-
.../Multiplayer/TestMultiplayerRoomManager.cs | 10 +--
.../Visual/OnlinePlay/TestRoomManager.cs | 13 ++-
21 files changed, 74 insertions(+), 263 deletions(-)
delete mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index 9951f62c77f6..d06a91433d1b 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -8,7 +8,6 @@
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
@@ -21,23 +20,13 @@ public partial class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen = null!;
- private Room? lastJoinedRoom;
- private string? lastJoinedPassword;
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen()));
-
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
-
- AddStep("bind to event", () =>
- {
- lastJoinedRoom = null;
- lastJoinedPassword = null;
- RoomManager.JoinRoomRequested = onRoomJoined;
- });
}
[Test]
@@ -46,9 +35,6 @@ public void TestJoinRoomWithoutPassword()
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
-
- AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
- AddAssert("room join password correct", () => lastJoinedPassword == null);
}
[Test]
@@ -126,9 +112,6 @@ public void TestJoinRoomWithCorrectPassword()
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
-
- AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
- AddAssert("room join password correct", () => lastJoinedPassword == "password");
}
[Test]
@@ -142,15 +125,6 @@ public void TestJoinRoomWithPasswordViaKeyboardOnly()
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press enter", () => InputManager.Key(Key.Enter));
-
- AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
- AddAssert("room join password correct", () => lastJoinedPassword == "password");
- }
-
- private void onRoomJoined(Room room, string? password)
- {
- lastJoinedRoom = room;
- lastJoinedPassword = password;
}
}
}
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index e5eade8c1da2..7dfe974651ac 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -253,6 +253,9 @@ protected virtual void OnRoomJoined()
public Task LeaveRoom()
{
+ if (Room == null)
+ return Task.CompletedTask;
+
// The join may have not completed yet, so certain tasks that either update the room or reference the room should be cancelled.
// This includes the setting of Room itself along with the initial update of the room settings on join.
joinCancellationSource?.Cancel();
diff --git a/osu.Game/Online/Rooms/CreateRoomRequest.cs b/osu.Game/Online/Rooms/CreateRoomRequest.cs
index 63a3b7bfa8b6..9773bb5e7d97 100644
--- a/osu.Game/Online/Rooms/CreateRoomRequest.cs
+++ b/osu.Game/Online/Rooms/CreateRoomRequest.cs
@@ -15,6 +15,7 @@ public class CreateRoomRequest : APIRequest
public CreateRoomRequest(Room room)
{
Room = room;
+ Success += r => Room.CopyFrom(r);
}
protected override WebRequest CreateWebRequest()
@@ -23,7 +24,6 @@ protected override WebRequest CreateWebRequest()
req.ContentType = "application/json";
req.Method = HttpMethod.Post;
-
req.AddRaw(JsonConvert.SerializeObject(Room));
return req;
diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs
index dfc7a53fb239..13e7ac8c84a6 100644
--- a/osu.Game/Online/Rooms/JoinRoomRequest.cs
+++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs
@@ -16,6 +16,7 @@ public JoinRoomRequest(Room room, string? password)
{
Room = room;
Password = password;
+ Success += r => Room.CopyFrom(r);
}
protected override WebRequest CreateWebRequest()
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index 73f980f0a378..3abb4098fb9f 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -5,12 +5,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Logging;
-using osu.Game.Online.API;
using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
@@ -23,89 +21,11 @@ public partial class RoomManager : Component, IRoomManager
public IBindableList Rooms => rooms;
- protected IBindable JoinedRoom => joinedRoom;
- private readonly Bindable joinedRoom = new Bindable();
-
- [Resolved]
- private IAPIProvider api { get; set; } = null!;
-
public RoomManager()
{
RelativeSizeAxes = Axes.Both;
}
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
- PartRoom();
- }
-
- public virtual void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
- {
- room.Host = api.LocalUser.Value;
-
- var req = new CreateRoomRequest(room);
-
- req.Success += result =>
- {
- joinedRoom.Value = room;
-
- AddOrUpdateRoom(result);
- room.CopyFrom(result); // Also copy back to the source model, since this is likely to have been stored elsewhere.
-
- // The server may not contain all properties (such as password), so invoke success with the given room.
- onSuccess?.Invoke(room);
- };
-
- req.Failure += exception =>
- {
- onError?.Invoke(req.Response?.Error ?? exception.Message);
- };
-
- api.Queue(req);
- }
-
- private JoinRoomRequest? currentJoinRoomRequest;
-
- public virtual void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
- {
- currentJoinRoomRequest?.Cancel();
- currentJoinRoomRequest = new JoinRoomRequest(room, password);
-
- currentJoinRoomRequest.Success += result =>
- {
- joinedRoom.Value = room;
-
- AddOrUpdateRoom(result);
- room.CopyFrom(result); // Also copy back to the source model, since this is likely to have been stored elsewhere.
-
- onSuccess?.Invoke(room);
- };
-
- currentJoinRoomRequest.Failure += exception =>
- {
- if (exception is OperationCanceledException)
- return;
-
- onError?.Invoke(exception.Message);
- };
-
- api.Queue(currentJoinRoomRequest);
- }
-
- public virtual void PartRoom()
- {
- currentJoinRoomRequest?.Cancel();
-
- if (joinedRoom.Value == null)
- return;
-
- if (api.State.Value == APIState.Online)
- api.Queue(new PartRoomRequest(joinedRoom.Value));
-
- joinedRoom.Value = null;
- }
-
private readonly HashSet ignoredRooms = new HashSet();
public void AddOrUpdateRoom(Room room)
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
index 13a282dd52ea..e3d6d42c055b 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
@@ -34,7 +34,6 @@
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.DailyChallenge.Events;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Match.Components;
@@ -71,9 +70,6 @@ public partial class DailyChallenge : OsuScreen, IPreviewTrackOwner, IHandlePres
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
- [Cached(Type = typeof(IRoomManager))]
- private RoomManager roomManager { get; set; }
-
[Cached]
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
@@ -115,7 +111,6 @@ public DailyChallenge(Room room)
{
this.room = room;
playlistItem = room.Playlist.Single();
- roomManager = new RoomManager();
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
}
@@ -131,7 +126,6 @@ private void load(AudioManager audio)
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- roomManager,
beatmapAvailabilityTracker,
new ScreenStack(new RoomBackgroundScreen(playlistItem))
{
@@ -426,7 +420,7 @@ public override void OnEntering(ScreenTransitionEvent e)
base.OnEntering(e);
waves.Show();
- roomManager.JoinRoom(room);
+ API.Queue(new JoinRoomRequest(room, null));
startLoopingTrack(this, musicController);
metadataClient.BeginWatchingMultiplayerRoom(room.RoomID!.Value).ContinueWith(t =>
@@ -480,7 +474,7 @@ public override bool OnExiting(ScreenExitEvent e)
previewTrackManager.StopAnyPlaying(this);
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
- roomManager.PartRoom();
+ API.Queue(new PartRoomRequest(room));
metadataClient.EndWatchingMultiplayerRoom(room.RoomID!.Value).FireAndForget();
return base.OnExiting(e);
diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
index ed4fb7b15e69..8ecb1dd7e0e9 100644
--- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
@@ -38,27 +38,5 @@ public interface IRoomManager
/// Removes all s from this .
///
void ClearRooms();
-
- ///
- /// Creates a new .
- ///
- /// The to create.
- /// An action to be invoked if the creation succeeds.
- /// An action to be invoked if an error occurred.
- void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null);
-
- ///
- /// Joins a .
- ///
- /// The to join. must be populated.
- /// An optional password to use for the join operation.
- ///
- ///
- void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null);
-
- ///
- /// Parts the currently-joined .
- ///
- void PartRoom();
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index f00cf7427c0a..f3f4df166a18 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -263,6 +263,9 @@ public override void OnResuming(ScreenTransitionEvent e)
music.EnsurePlayingSomething();
onReturning();
+
+ // Poll for any newly-created rooms (including potentially the user's own).
+ ListingPollingComponent.PollImmediately();
}
public override bool OnExiting(ScreenExitEvent e)
@@ -297,14 +300,14 @@ private void onLeaving()
popoverContainer.HidePopover();
}
- public virtual void Join(Room room, string? password, Action? onSuccess = null, Action? onFailure = null) => Schedule(() =>
+ public void Join(Room room, string? password, Action? onSuccess = null, Action? onFailure = null) => Schedule(() =>
{
if (joiningRoomOperation != null)
return;
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
- RoomManager?.JoinRoom(room, password, _ =>
+ TryJoin(room, password, r =>
{
Open(room);
joiningRoomOperation?.Dispose();
@@ -318,6 +321,8 @@ public virtual void Join(Room room, string? password, Action? onSuccess =
});
});
+ protected abstract void TryJoin(Room room, string? password, Action onSuccess, Action onFailure);
+
///
/// Copies a room and opens it as a fresh (not-yet-created) one.
///
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index 4ef31c02c3ac..d37f3b877c26 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -343,7 +343,9 @@ public override bool OnExiting(ScreenExitEvent e)
if (!ensureExitConfirmed())
return true;
- RoomManager?.PartRoom();
+ if (Room.RoomID != null)
+ PartRoom();
+
Mods.Value = Array.Empty();
onLeaving();
@@ -351,6 +353,8 @@ public override bool OnExiting(ScreenExitEvent e)
return base.OnExiting(e);
}
+ protected abstract void PartRoom();
+
private bool ensureExitConfirmed()
{
if (ExitConfirmed)
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
index bf316bb3da5b..dfed32aebccd 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
@@ -8,7 +8,6 @@
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
@@ -97,8 +96,6 @@ private void transitionFromResults()
protected override string ScreenTitle => "Multiplayer";
- protected override RoomManager CreateRoomManager() => new MultiplayerRoomManager();
-
protected override LoungeSubScreen CreateLounge() => new MultiplayerLoungeSubScreen();
public void Join(Room room, string? password) => Schedule(() => Lounge.Join(room, password));
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index dd61caa3db37..e901ecbdce51 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -1,12 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Logging;
-using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Configuration;
@@ -32,19 +33,6 @@ public partial class MultiplayerLoungeSubScreen : LoungeSubScreen
private Dropdown roomAccessTypeDropdown = null!;
private OsuCheckbox showInProgress = null!;
- public override void OnResuming(ScreenTransitionEvent e)
- {
- base.OnResuming(e);
-
- // Upon having left a room, we don't know whether we were the only participant, and whether the room is now closed as a result of leaving it.
- // To work around this, temporarily remove the room and trigger an immediate listing poll.
- if (e.Last is MultiplayerMatchSubScreen match)
- {
- RoomManager?.RemoveRoom(match.Room);
- ListingPollingComponent.PollImmediately();
- }
- }
-
protected override IEnumerable CreateFilterControls()
{
foreach (var control in base.CreateFilterControls())
@@ -93,6 +81,24 @@ protected override FilterCriteria CreateFilterCriteria()
protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
+ protected override void TryJoin(Room room, string? password, Action onSuccess, Action onFailure)
+ {
+ client.JoinRoom(room, password).ContinueWith(result =>
+ {
+ if (result.IsCompletedSuccessfully)
+ onSuccess(room);
+ else
+ {
+ const string message = "Failed to join multiplayer room.";
+
+ if (result.Exception != null)
+ Logger.Error(result.Exception, message);
+
+ onFailure.Invoke(result.Exception?.AsSingular().Message ?? message);
+ }
+ });
+ }
+
protected override void OpenNewRoom(Room room)
{
if (!client.IsConnected.Value)
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index 06ea5ee033d1..553c0c9182e9 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -278,6 +278,8 @@ public override bool OnExiting(ScreenExitEvent e)
return base.OnExiting(e);
}
+ protected override void PartRoom() => client.LeaveRoom();
+
private ModSettingChangeTracker? modSettingChangeTracker;
private ScheduledDelegate? debouncedModSettingsUpdate;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
deleted file mode 100644
index 7f09c9cbe972..000000000000
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System;
-using System.Diagnostics;
-using osu.Framework.Allocation;
-using osu.Framework.Extensions.ExceptionExtensions;
-using osu.Framework.Logging;
-using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
-
-namespace osu.Game.Screens.OnlinePlay.Multiplayer
-{
- public partial class MultiplayerRoomManager : RoomManager
- {
- [Resolved]
- private MultiplayerClient multiplayerClient { get; set; } = null!;
-
- public override void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
- => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password, onSuccess, onError), onError);
-
- public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
- {
- if (!multiplayerClient.IsConnected.Value)
- {
- onError?.Invoke("Not currently connected to the multiplayer server.");
- return;
- }
-
- // this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join.
- // should probably be done at a higher level, but due to the current structure of things this is the easiest place for now.
- if (room.HasEnded)
- {
- onError?.Invoke("Cannot join an ended room.");
- return;
- }
-
- base.JoinRoom(room, password, r => joinMultiplayerRoom(r, password, onSuccess, onError), onError);
- }
-
- public override void PartRoom()
- {
- if (JoinedRoom.Value == null)
- return;
-
- base.PartRoom();
- multiplayerClient.LeaveRoom();
- }
-
- private void joinMultiplayerRoom(Room room, string? password, Action? onSuccess = null, Action? onError = null)
- {
- Debug.Assert(room.RoomID != null);
-
- multiplayerClient.JoinRoom(room, password).ContinueWith(t =>
- {
- if (t.IsCompletedSuccessfully)
- Schedule(() => onSuccess?.Invoke(room));
- else if (t.IsFaulted)
- {
- const string message = "Failed to join multiplayer room.";
-
- if (t.Exception != null)
- Logger.Error(t.Exception, message);
-
- PartRoom();
- Schedule(() => onError?.Invoke(t.Exception?.AsSingular().Message ?? message));
- }
- });
- }
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
index 17fb667e1400..16462b90c121 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -36,12 +36,12 @@ public abstract partial class OnlinePlayScreen : OsuScreen, IHasSubScreenStack
private readonly ScreenStack screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both };
private OnlinePlayScreenWaveContainer waves = null!;
- [Cached(Type = typeof(IRoomManager))]
- protected RoomManager RoomManager { get; private set; }
-
[Cached]
private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker();
+ [Cached(Type = typeof(IRoomManager))]
+ private readonly RoomManager roomManager = new RoomManager();
+
[Resolved]
protected IAPIProvider API { get; private set; } = null!;
@@ -51,8 +51,6 @@ protected OnlinePlayScreen()
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
-
- RoomManager = CreateRoomManager();
}
private readonly IBindable apiState = new Bindable();
@@ -67,7 +65,7 @@ private void load()
{
screenStack,
new Header(ScreenTitle, screenStack),
- RoomManager,
+ roomManager,
ongoingOperationTracker,
}
};
@@ -165,8 +163,6 @@ public override bool OnExiting(ScreenExitEvent e)
subScreen.Exit();
}
- RoomManager.PartRoom();
-
waves.Hide();
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs
index fa1ee004c9bc..9b35a794a30a 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
@@ -15,9 +14,6 @@ public abstract partial class OnlinePlaySubScreen : OsuScreen, IOnlinePlaySubScr
protected sealed override bool PlayExitSound => false;
- [Resolved]
- protected IRoomManager? RoomManager { get; private set; }
-
protected OnlinePlaySubScreen()
{
Anchor = Anchor.Centre;
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index d66b4f844c48..92415e0eb180 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@@ -59,6 +60,20 @@ protected override FilterCriteria CreateFilterCriteria()
return criteria;
}
+ protected override void TryJoin(Room room, string? password, Action onSuccess, Action onFailure)
+ {
+ var joinRoomRequest = new JoinRoomRequest(room, password);
+
+ joinRoomRequest.Success += r => onSuccess(r);
+ joinRoomRequest.Failure += exception =>
+ {
+ if (exception is not OperationCanceledException)
+ onFailure(exception.Message);
+ };
+
+ api.Queue(joinRoomRequest);
+ }
+
protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton();
protected override Room CreateNewRoom()
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
index 88af161cc847..b3d1d577ed67 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
@@ -75,9 +75,6 @@ protected partial class MatchSettings : CompositeDrawable
private PurpleRoundedButton editPlaylistButton = null!;
- [Resolved]
- private IRoomManager? manager { get; set; }
-
[Resolved]
private IAPIProvider api { get; set; } = null!;
@@ -449,7 +446,11 @@ private void apply()
room.Duration = DurationField.Current.Value;
loadingLayer.Show();
- manager?.CreateRoom(room, onSuccess, onError);
+
+ var req = new CreateRoomRequest(room);
+ req.Success += onSuccess;
+ req.Failure += e => onError(req.Response?.Error ?? e.Message);
+ api.Queue(req);
}
private void hideError() => ErrorText.FadeOut(50);
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 9b4630ac0b09..064c355a69f4 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -290,6 +290,8 @@ private void closePlaylist()
}));
}
+ protected override void PartRoom() => api.Queue(new PartRoomRequest(Room));
+
protected override Screen CreateGameplayScreen(PlaylistItem selectedItem)
{
return new PlayerLoader(() => new PlaylistsPlayer(Room, selectedItem)
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
index 42cf31782946..dca1fc8f3ca7 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -56,7 +56,7 @@ public override void SetUpSteps()
AddStep("join room", () =>
{
SelectedRoom.Value = CreateRoom();
- RoomManager.CreateRoom(SelectedRoom.Value);
+ API.Queue(new CreateRoomRequest(SelectedRoom.Value));
});
AddUntilStep("wait for room join", () => RoomJoined);
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
index b998a638e5b5..59ac9a97492f 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
@@ -1,12 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
-using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer
@@ -15,7 +13,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
/// A for use in multiplayer test scenes.
/// Should generally not be used by itself outside of a .
///
- public partial class TestMultiplayerRoomManager : MultiplayerRoomManager
+ public partial class TestMultiplayerRoomManager : RoomManager
{
private readonly TestRoomRequestsHandler requestsHandler;
@@ -26,12 +24,6 @@ public TestMultiplayerRoomManager(TestRoomRequestsHandler requestsHandler)
public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms;
- public override void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
- => base.CreateRoom(room, r => onSuccess?.Invoke(r), onError);
-
- public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
- => base.JoinRoom(room, password, r => onSuccess?.Invoke(r), onError);
-
///
/// Adds a room to a local "server-side" list that's returned when a is fired.
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
index b1e3eafacc3e..60d169a46fd2 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
@@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework.Allocation;
using osu.Game.Beatmaps;
+using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
@@ -15,15 +17,10 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
public partial class TestRoomManager : RoomManager
{
- public Action? JoinRoomRequested;
-
private int currentRoomId;
- public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
- {
- JoinRoomRequested?.Invoke(room, password);
- base.JoinRoom(room, password, onSuccess, onError);
- }
+ [Resolved]
+ private IAPIProvider api { get; set; } = null!;
public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withSpotlightRooms = false)
{
@@ -49,7 +46,7 @@ public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword =
public void AddRoom(Room room)
{
room.RoomID = -currentRoomId;
- CreateRoom(room);
+ api.Queue(new CreateRoomRequest(room));
currentRoomId++;
}
}
From 9a623257f5bd8cfed7f2d691fbb1c2959483c111 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 16:19:09 +0900
Subject: [PATCH 06/13] Adjust + fix tests
---
.../StatefulMultiplayerClientTest.cs | 7 +-
.../TestSceneDrawableLoungeRoom.cs | 2 +-
.../Multiplayer/TestSceneMultiplayer.cs | 15 ++--
.../TestSceneMultiplayerLoungeSubScreen.cs | 58 +++++++++++---
.../TestSceneMultiplayerPlaylist.cs | 10 +--
.../TestScenePlaylistsLoungeSubScreen.cs | 30 ++++++-
.../TestScenePlaylistsMatchSettingsOverlay.cs | 78 +++++++------------
.../Visual/TestMultiplayerComponents.cs | 24 ++----
osu.Game/Online/Rooms/Room.cs | 17 ++++
.../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 14 +---
.../OnlinePlay/Lounge/IOnlinePlayLounge.cs | 32 ++++++++
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 19 +++--
.../Screens/OnlinePlay/OnlinePlayScreen.cs | 2 -
.../IMultiplayerTestSceneDependencies.cs | 6 --
.../Multiplayer/MultiplayerTestScene.cs | 3 +-
.../MultiplayerTestSceneDependencies.cs | 6 +-
.../Multiplayer/TestMultiplayerClient.cs | 35 +++++++--
.../Multiplayer/TestMultiplayerRoomManager.cs | 34 --------
.../OnlinePlayTestSceneDependencies.cs | 4 +-
.../Visual/OnlinePlay/TestRoomManager.cs | 20 +++--
20 files changed, 232 insertions(+), 184 deletions(-)
create mode 100644 osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
delete mode 100644 osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
index 559db1675180..be30e06ed4b6 100644
--- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
+++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
@@ -8,7 +8,6 @@
using osu.Framework.Testing;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Rooms;
using osu.Game.Tests.Visual.Multiplayer;
namespace osu.Game.Tests.NonVisual.Multiplayer
@@ -72,10 +71,6 @@ public void TestPlayingUsersUpdatedOnJoin()
AddStep("create room initially in gameplay", () =>
{
- var newRoom = new Room();
- newRoom.CopyFrom(SelectedRoom.Value!);
-
- newRoom.RoomID = null;
MultiplayerClient.RoomSetupAction = room =>
{
room.State = MultiplayerRoomState.Playing;
@@ -86,7 +81,7 @@ public void TestPlayingUsersUpdatedOnJoin()
});
};
- RoomManager.CreateRoom(newRoom);
+ MultiplayerClient.JoinRoom(MultiplayerClient.ServerSideRooms.Single()).ConfigureAwait(false);
});
AddUntilStep("wait for room join", () => RoomJoined);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs
index c5fb52461a34..459a90d0960e 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs
@@ -38,7 +38,7 @@ public partial class TestSceneDrawableLoungeRoom : OsuManualInputManagerTestScen
[BackgroundDependencyLoader]
private void load()
{
- var mockLounge = new Mock();
+ var mockLounge = new Mock();
mockLounge
.Setup(l => l.Join(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>()))
.Callback, Action>((_, _, _, d) =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index fb653cea8bd1..0966c61a3aa6 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -58,7 +58,6 @@ public partial class TestSceneMultiplayer : ScreenTestScene
private TestMultiplayerComponents multiplayerComponents = null!;
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
- private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
[Resolved]
private OsuConfigManager config { get; set; } = null!;
@@ -257,7 +256,7 @@ public void TestExitMidJoin()
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Playlist =
@@ -286,7 +285,7 @@ public void TestJoinRoomWithoutPassword()
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Playlist =
@@ -336,7 +335,7 @@ public void TestJoinRoomWithPassword()
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Password = "password",
@@ -789,7 +788,7 @@ public void TestRoomSettingsReQueriedWhenJoiningRoom()
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
QueueMode = QueueMode.AllPlayers,
@@ -810,8 +809,8 @@ public void TestRoomSettingsReQueriedWhenJoiningRoom()
AddStep("disable polling", () => this.ChildrenOfType().Single().TimeBetweenPolls.Value = 0);
AddStep("change server-side settings", () =>
{
- roomManager.ServerSideRooms[0].Name = "New name";
- roomManager.ServerSideRooms[0].Playlist =
+ multiplayerClient.ServerSideRooms[0].Name = "New name";
+ multiplayerClient.ServerSideRooms[0].Playlist =
[
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
@@ -828,7 +827,7 @@ public void TestRoomSettingsReQueriedWhenJoiningRoom()
AddAssert("local room has correct settings", () =>
{
var localRoom = this.ChildrenOfType().Single().Room;
- return localRoom.Name == roomManager.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2;
+ return localRoom.Name == multiplayerClient.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2;
});
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index d06a91433d1b..4a259149e254 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -9,18 +9,26 @@
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.OnlinePlay.Lounge;
+using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
- public partial class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
+ public partial class TestSceneMultiplayerLoungeSubScreen : MultiplayerTestScene
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen = null!;
+ private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First();
+
+ public TestSceneMultiplayerLoungeSubScreen()
+ : base(false)
+ {
+ }
+
public override void SetUpSteps()
{
base.SetUpSteps();
@@ -32,15 +40,17 @@ public override void SetUpSteps()
[Test]
public void TestJoinRoomWithoutPassword()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false));
+ addRoom(false);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
+
+ AddAssert("room joined", () => MultiplayerClient.RoomJoined);
}
[Test]
public void TestPopoverHidesOnBackButton()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
@@ -53,18 +63,22 @@ public void TestPopoverHidesOnBackButton()
AddStep("hit escape", () => InputManager.Key(Key.Escape));
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
+
+ AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
}
[Test]
public void TestPopoverHidesOnLeavingScreen()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
AddStep("exit screen", () => Stack.Exit());
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
+
+ AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
}
[Test]
@@ -72,16 +86,18 @@ public void TestJoinRoomWithIncorrectPasswordViaButton()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "wrong");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
- AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
+ AddAssert("still at lounge", () => loungeScreen.IsCurrentScreen());
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
+
+ AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
}
[Test]
@@ -89,16 +105,18 @@ public void TestJoinRoomWithIncorrectPasswordViaEnter()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "wrong");
AddStep("press enter", () => InputManager.Key(Key.Enter));
- AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
+ AddAssert("still at lounge", () => loungeScreen.IsCurrentScreen());
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
+
+ AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
}
[Test]
@@ -106,12 +124,14 @@ public void TestJoinRoomWithCorrectPassword()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
+
+ AddUntilStep("room joined", () => MultiplayerClient.RoomJoined);
}
[Test]
@@ -119,12 +139,30 @@ public void TestJoinRoomWithPasswordViaKeyboardOnly()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ addRoom(true);
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press enter", () => InputManager.Key(Key.Enter));
+
+ AddAssert("room joined", () => MultiplayerClient.RoomJoined);
+ }
+
+ private void addRoom(bool withPassword)
+ {
+ int initialRoomCount = 0;
+
+ AddStep("add room", () =>
+ {
+ initialRoomCount = roomsContainer.Rooms.Count;
+ RoomManager.AddRooms(1, withPassword: withPassword);
+ loungeScreen.RefreshRooms();
+ });
+
+ AddUntilStep("wait for room to appear", () => roomsContainer.Rooms.Count == initialRoomCount + 1);
}
+
+ protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
index 36f5bba38436..77b75f407b33 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
@@ -127,7 +127,7 @@ public void TestListsClearedWhenRoomLeft()
addItemStep();
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
- AddStep("leave room", () => RoomManager.PartRoom());
+ AddStep("leave room", () => MultiplayerClient.LeaveRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
AddUntilStep("item 0 not in lists", () => !inHistoryList(0) && !inQueueList(0));
@@ -148,7 +148,7 @@ public void TestQueueTabCount()
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
assertQueueTabCount(2);
- AddStep("leave room", () => RoomManager.PartRoom());
+ AddStep("leave room", () => MultiplayerClient.LeaveRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
assertQueueTabCount(0);
}
@@ -157,12 +157,12 @@ public void TestQueueTabCount()
[Test]
public void TestJoinRoomWithMixedItemsAddedInCorrectLists()
{
- AddStep("leave room", () => RoomManager.PartRoom());
+ AddStep("leave room", () => MultiplayerClient.LeaveRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
AddStep("join room with items", () =>
{
- RoomManager.CreateRoom(new Room
+ API.Queue(new CreateRoomRequest(new Room
{
Name = "test name",
Playlist =
@@ -177,7 +177,7 @@ public void TestJoinRoomWithMixedItemsAddedInCorrectLists()
Expired = true
}
]
- });
+ }));
});
AddUntilStep("wait for room join", () => RoomJoined);
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index 8c8dc8d69ae6..0897a3b2f568 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -35,7 +35,13 @@ public override void SetUpSteps()
[Test]
public void TestManyRooms()
{
- AddStep("add rooms", () => RoomManager.AddRooms(500));
+ AddStep("add rooms", () =>
+ {
+ RoomManager.AddRooms(500);
+ loungeScreen.RefreshRooms();
+ });
+
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 500);
}
[Test]
@@ -43,7 +49,12 @@ public void TestScrollByDraggingRooms()
{
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
- AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddStep("add rooms", () =>
+ {
+ RoomManager.AddRooms(30);
+ loungeScreen.RefreshRooms();
+ });
+
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -60,7 +71,12 @@ public void TestScrollByDraggingRooms()
[Test]
public void TestScrollSelectedIntoView()
{
- AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddStep("add rooms", () =>
+ {
+ RoomManager.AddRooms(30);
+ loungeScreen.RefreshRooms();
+ });
+
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -74,7 +90,13 @@ public void TestScrollSelectedIntoView()
[Test]
public void TestEnteringRoomTakesLeaseOnSelection()
{
- AddStep("add rooms", () => RoomManager.AddRooms(1));
+ AddStep("add rooms", () =>
+ {
+ RoomManager.AddRooms(1);
+ loungeScreen.RefreshRooms();
+ });
+
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 1);
AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 58683314518e..51e39e1b7f95 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -3,14 +3,13 @@
using System;
using NUnit.Framework;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Tests.Visual.OnlinePlay;
@@ -21,13 +20,33 @@ public partial class TestScenePlaylistsMatchSettingsOverlay : OnlinePlayTestScen
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private TestRoomSettings settings = null!;
-
- protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
+ private Func? handleRequest;
public override void SetUpSteps()
{
base.SetUpSteps();
+ AddStep("setup api", () =>
+ {
+ handleRequest = null;
+ ((DummyAPIAccess)API).HandleRequest = req =>
+ {
+ if (req is not CreateRoomRequest createReq || handleRequest == null)
+ return false;
+
+ if (handleRequest(createReq.Room) is string errorText)
+ createReq.TriggerFailure(new APIException(errorText, null));
+ else
+ {
+ var createdRoom = new APICreatedRoom();
+ createdRoom.CopyFrom(createReq.Room);
+ createReq.TriggerSuccess(createdRoom);
+ }
+
+ return true;
+ };
+ });
+
AddStep("create overlay", () =>
{
SelectedRoom.Value = new Room();
@@ -75,10 +94,10 @@ public void TestCorrectSettingsApplied()
settings.DurationField.Current.Value = expectedDuration;
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
- RoomManager.CreateRequested = r =>
+ handleRequest = r =>
{
createdRoom = r;
- return string.Empty;
+ return null;
};
});
@@ -103,7 +122,7 @@ public void TestInvalidBeatmapError()
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
- RoomManager.CreateRequested = _ => errorMessage;
+ handleRequest = _ => errorMessage;
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
@@ -128,7 +147,7 @@ public void TestCreationFailureDisplaysError()
SelectedRoom.Value!.Name = "Test Room";
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
- RoomManager.CreateRequested = _ => failText;
+ handleRequest = _ => failText;
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
@@ -159,48 +178,5 @@ public TestRoomSettings(Room room)
{
}
}
-
- private class TestDependencies : OnlinePlayTestSceneDependencies
- {
- protected override IRoomManager CreateRoomManager() => new TestRoomManager();
- }
-
- protected class TestRoomManager : IRoomManager
- {
- public Func? CreateRequested;
-
- public event Action RoomsUpdated
- {
- add { }
- remove { }
- }
-
- public IBindable InitialRoomsReceived { get; } = new Bindable(true);
-
- public IBindableList Rooms => null!;
-
- public void AddOrUpdateRoom(Room room) => throw new NotImplementedException();
-
- public void RemoveRoom(Room room) => throw new NotImplementedException();
-
- public void ClearRooms() => throw new NotImplementedException();
-
- public void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
- {
- if (CreateRequested == null)
- return;
-
- string error = CreateRequested.Invoke(room);
-
- if (!string.IsNullOrEmpty(error))
- onError?.Invoke(error);
- else
- onSuccess?.Invoke(room);
- }
-
- public void JoinRoom(Room room, string? password, Action? onSuccess = null, Action? onError = null) => throw new NotImplementedException();
-
- public void PartRoom() => throw new NotImplementedException();
- }
}
}
diff --git a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
index 1814fb70c8a6..e385ff3a03a0 100644
--- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
+++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
@@ -11,7 +11,6 @@
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Visual.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
@@ -26,15 +25,12 @@ namespace osu.Game.Tests.Visual
/// - Provides a to be resolved as a dependency in the screen,
/// which is typically a part of .
/// - Rebinds the to handle requests via a .
- /// - Provides a for the screen.
///
///
///
public partial class TestMultiplayerComponents : OsuScreen
{
- public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen => multiplayerScreen;
-
- public TestMultiplayerRoomManager RoomManager => multiplayerScreen.RoomManager;
+ public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen { get; }
public IScreen CurrentScreen => screenStack.CurrentScreen;
@@ -53,17 +49,17 @@ public partial class TestMultiplayerComponents : OsuScreen
private BeatmapManager beatmapManager { get; set; }
private readonly OsuScreenStack screenStack;
- private readonly TestMultiplayer multiplayerScreen;
+ private readonly TestRoomRequestsHandler requestsHandler = new TestRoomRequestsHandler();
public TestMultiplayerComponents()
{
- multiplayerScreen = new TestMultiplayer();
+ MultiplayerScreen = new Screens.OnlinePlay.Multiplayer.Multiplayer();
InternalChildren = new Drawable[]
{
userLookupCache,
beatmapLookupCache,
- MultiplayerClient = new TestMultiplayerClient(RoomManager),
+ MultiplayerClient = new TestMultiplayerClient(requestsHandler),
screenStack = new OsuScreenStack
{
Name = nameof(TestMultiplayerComponents),
@@ -71,13 +67,13 @@ public TestMultiplayerComponents()
}
};
- screenStack.Push(multiplayerScreen);
+ screenStack.Push(MultiplayerScreen);
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
{
- ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager);
+ ((DummyAPIAccess)api).HandleRequest = request => requestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager);
}
public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton();
@@ -90,13 +86,5 @@ public override bool OnExiting(ScreenExitEvent e)
screenStack.Exit();
return true;
}
-
- private partial class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
- {
- public new TestMultiplayerRoomManager RoomManager { get; private set; }
- public TestRoomRequestsHandler RequestsHandler { get; private set; }
-
- protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(RequestsHandler = new TestRoomRequestsHandler());
- }
}
}
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index f8660a656e6f..c5e292a19d35 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,6 +342,23 @@ public RoomAvailability Availability
// Not yet serialised (not implemented).
private RoomAvailability availability;
+ public Room()
+ {
+ }
+
+ public Room(MultiplayerRoom room)
+ {
+ RoomID = room.RoomID;
+ Name = room.Settings.Name;
+ Password = room.Settings.Password;
+ Type = room.Settings.MatchType;
+ QueueMode = room.Settings.QueueMode;
+ AutoStartDuration = room.Settings.AutoStartDuration;
+ AutoSkip = room.Settings.AutoSkip;
+ Host = room.Host != null ? new APIUser { Id = room.Host.UserID } : null;
+ Playlist = room.Playlist.Select(p => new PlaylistItem(p)).ToArray();
+ }
+
///
/// Copies values from another into this one.
///
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
index 0a55472c2d75..032a231ad3f7 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
@@ -24,7 +24,6 @@
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Components;
@@ -51,7 +50,7 @@ public required Bindable SelectedRoom
}
[Resolved(canBeNull: true)]
- private LoungeSubScreen? lounge { get; set; }
+ private IOnlinePlayLounge? lounge { get; set; }
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
@@ -163,7 +162,7 @@ public MenuItem[] ContextMenuItems
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
{
- lounge?.OpenCopy(Room);
+ lounge?.Clone(Room);
})
};
@@ -171,12 +170,7 @@ public MenuItem[] ContextMenuItems
{
items.Add(new OsuMenuItem("Close playlist", MenuItemType.Destructive, () =>
{
- dialogOverlay?.Push(new ClosePlaylistDialog(Room, () =>
- {
- var request = new ClosePlaylistRequest(Room.RoomID!.Value);
- request.Success += () => lounge?.RefreshRooms();
- api.Queue(request);
- }));
+ dialogOverlay?.Push(new ClosePlaylistDialog(Room, () => lounge?.Close(Room)));
}));
}
@@ -239,7 +233,7 @@ public partial class PasswordEntryPopover : OsuPopover
private readonly Room room;
[Resolved(canBeNull: true)]
- private LoungeSubScreen? lounge { get; set; }
+ private IOnlinePlayLounge? lounge { get; set; }
public override bool HandleNonPositionalInput => true;
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs b/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
new file mode 100644
index 000000000000..8fa7d0751f0f
--- /dev/null
+++ b/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Online.Rooms;
+
+namespace osu.Game.Screens.OnlinePlay.Lounge
+{
+ public interface IOnlinePlayLounge
+ {
+ ///
+ /// Attempts to join the given room.
+ ///
+ /// The room to join.
+ /// The password.
+ /// A delegate to invoke if the user joined the room.
+ /// A delegate to invoke if the user is not able join the room.
+ void Join(Room room, string? password, Action? onSuccess = null, Action? onFailure = null);
+
+ ///
+ /// Clones the given room and opens it as a fresh (not-yet-created) one.
+ ///
+ /// The room to clone.
+ void Clone(Room room);
+
+ ///
+ /// Closes the given room.
+ ///
+ /// The room to close.
+ void Close(Room room);
+ }
+}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index f3f4df166a18..df17063fdf79 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -21,6 +21,7 @@
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets;
@@ -33,7 +34,8 @@
namespace osu.Game.Screens.OnlinePlay.Lounge
{
[Cached]
- public abstract partial class LoungeSubScreen : OnlinePlaySubScreen
+ [Cached(typeof(IOnlinePlayLounge))]
+ public abstract partial class LoungeSubScreen : OnlinePlaySubScreen, IOnlinePlayLounge
{
public override string Title => "Lounge";
@@ -323,11 +325,7 @@ public void Join(Room room, string? password, Action? onSuccess = null, Ac
protected abstract void TryJoin(Room room, string? password, Action onSuccess, Action onFailure);
- ///
- /// Copies a room and opens it as a fresh (not-yet-created) one.
- ///
- /// The room to copy.
- public void OpenCopy(Room room)
+ public void Clone(Room room)
{
Debug.Assert(room.RoomID != null);
@@ -363,6 +361,15 @@ public void OpenCopy(Room room)
api.Queue(req);
}
+ public void Close(Room room)
+ {
+ Debug.Assert(room.RoomID != null);
+
+ var request = new ClosePlaylistRequest(room.RoomID.Value);
+ request.Success += RefreshRooms;
+ api.Queue(request);
+ }
+
///
/// Push a room as a new subscreen.
///
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
index 16462b90c121..8988c82deed3 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -220,8 +220,6 @@ private void subScreenChanged(IScreen lastScreen, IScreen newScreen)
protected abstract string ScreenTitle { get; }
- protected virtual RoomManager CreateRoomManager() => new RoomManager();
-
protected abstract LoungeSubScreen CreateLounge();
ScreenStack IHasSubScreenStack.SubScreenStack => screenStack;
diff --git a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
index efd0b80ebf96..262816ae89b7 100644
--- a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Screens.OnlinePlay;
using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Tests.Visual.Spectator;
@@ -17,11 +16,6 @@ public interface IMultiplayerTestSceneDependencies : IOnlinePlayTestSceneDepende
///
TestMultiplayerClient MultiplayerClient { get; }
- ///
- /// The cached .
- ///
- new TestMultiplayerRoomManager RoomManager { get; }
-
///
/// The cached .
///
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
index dca1fc8f3ca7..d1497d5142ce 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -17,7 +17,6 @@ public abstract partial class MultiplayerTestScene : OnlinePlayTestScene, IMulti
public const int PLAYER_2_ID = 56;
public TestMultiplayerClient MultiplayerClient => OnlinePlayDependencies.MultiplayerClient;
- public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager;
public TestSpectatorClient SpectatorClient => OnlinePlayDependencies.SpectatorClient;
protected new MultiplayerTestSceneDependencies OnlinePlayDependencies => (MultiplayerTestSceneDependencies)base.OnlinePlayDependencies;
@@ -56,7 +55,7 @@ public override void SetUpSteps()
AddStep("join room", () =>
{
SelectedRoom.Value = CreateRoom();
- API.Queue(new CreateRoomRequest(SelectedRoom.Value));
+ MultiplayerClient.CreateRoom(SelectedRoom.Value).ConfigureAwait(false);
});
AddUntilStep("wait for room join", () => RoomJoined);
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
index 88202d432764..24c33f2f49f5 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs
@@ -3,7 +3,6 @@
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Spectator;
-using osu.Game.Screens.OnlinePlay;
using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Tests.Visual.Spectator;
@@ -16,19 +15,16 @@ public class MultiplayerTestSceneDependencies : OnlinePlayTestSceneDependencies,
{
public TestMultiplayerClient MultiplayerClient { get; }
public TestSpectatorClient SpectatorClient { get; }
- public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager;
public MultiplayerTestSceneDependencies()
{
- MultiplayerClient = new TestMultiplayerClient(RoomManager);
+ MultiplayerClient = new TestMultiplayerClient(RequestsHandler);
SpectatorClient = CreateSpectatorClient();
CacheAs(MultiplayerClient);
CacheAs(SpectatorClient);
}
- protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(RequestsHandler);
-
protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient();
}
}
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index 70e298f3e045..d514fc0d7e7c 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -10,6 +10,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
+using osu.Game.Beatmaps;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
@@ -17,6 +18,7 @@
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Mods;
+using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer
{
@@ -65,15 +67,15 @@ public partial class TestMultiplayerClient : MultiplayerClient
[Resolved]
private IAPIProvider api { get; set; } = null!;
- private readonly TestMultiplayerRoomManager roomManager;
-
private MultiplayerPlaylistItem? currentItem => ServerRoom?.Playlist[currentIndex];
private int currentIndex;
private long lastPlaylistItemId;
- public TestMultiplayerClient(TestMultiplayerRoomManager roomManager)
+ private readonly TestRoomRequestsHandler apiRequestHandler;
+
+ public TestMultiplayerClient(TestRoomRequestsHandler? apiRequestHandler = null)
{
- this.roomManager = roomManager;
+ this.apiRequestHandler = apiRequestHandler ?? new TestRoomRequestsHandler();
}
public void Connect() => isConnected.Value = true;
@@ -214,7 +216,7 @@ protected override async Task JoinRoom(long roomId, string? pas
roomId = clone(roomId);
password = clone(password);
- ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID == roomId);
+ ServerAPIRoom = ServerSideRooms.Single(r => r.RoomID == roomId);
if (password != ServerAPIRoom.Password)
throw new InvalidOperationException("Invalid password.");
@@ -485,7 +487,15 @@ public async Task RemoveUserPlaylistItem(int userId, long playlistItemId)
protected override Task CreateRoom(MultiplayerRoom room)
{
- throw new NotImplementedException();
+ Room apiRoom = new Room(room)
+ {
+ Type = room.Settings.MatchType == MatchType.Playlists
+ ? MatchType.HeadToHead
+ : room.Settings.MatchType
+ };
+
+ AddServerSideRoom(apiRoom, api.LocalUser.Value);
+ return JoinRoom(apiRoom.RoomID!.Value, room.Settings.Password);
}
private async Task changeMatchType(MatchType type)
@@ -680,5 +690,18 @@ public override Task DisconnectInternal()
isConnected.Value = false;
return Task.CompletedTask;
}
+
+ #region API Room Handling
+
+ public IReadOnlyList ServerSideRooms
+ => apiRequestHandler.ServerSideRooms;
+
+ public void AddServerSideRoom(Room room, APIUser host)
+ => apiRequestHandler.AddServerSideRoom(room, host);
+
+ public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager beatmapManager)
+ => apiRequestHandler.HandleRequest(request, localUser, beatmapManager);
+
+ #endregion
}
}
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
deleted file mode 100644
index 59ac9a97492f..000000000000
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Collections.Generic;
-using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
-using osu.Game.Tests.Visual.OnlinePlay;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- ///
- /// A for use in multiplayer test scenes.
- /// Should generally not be used by itself outside of a .
- ///
- public partial class TestMultiplayerRoomManager : RoomManager
- {
- private readonly TestRoomRequestsHandler requestsHandler;
-
- public TestMultiplayerRoomManager(TestRoomRequestsHandler requestsHandler)
- {
- this.requestsHandler = requestsHandler;
- }
-
- public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms;
-
- ///
- /// Adds a room to a local "server-side" list that's returned when a is fired.
- ///
- /// The room.
- /// The host.
- public void AddServerSideRoom(Room room, APIUser host) => requestsHandler.AddServerSideRoom(room, host);
- }
-}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
index e2670c9ad890..203922c0576f 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -40,7 +40,7 @@ public OnlinePlayTestSceneDependencies()
RequestsHandler = new TestRoomRequestsHandler();
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
- RoomManager = CreateRoomManager();
+ RoomManager = new TestRoomManager();
UserLookupCache = new TestUserLookupCache();
BeatmapLookupCache = new BeatmapLookupCache();
@@ -80,7 +80,5 @@ protected void CacheAs(T instance)
if (instance is Drawable drawable)
drawableComponents.Add(drawable);
}
-
- protected virtual IRoomManager CreateRoomManager() => new TestRoomManager();
}
}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
index 60d169a46fd2..bff275392946 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
@@ -22,8 +22,14 @@ public partial class TestRoomManager : RoomManager
[Resolved]
private IAPIProvider api { get; set; } = null!;
+ [Resolved]
+ private RulesetStore rulesets { get; set; } = null!;
+
public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withSpotlightRooms = false)
{
+ // Can't reference Osu ruleset project here.
+ ruleset ??= rulesets.GetRuleset(0)!;
+
for (int i = 0; i < count; i++)
{
AddRoom(new Room
@@ -33,12 +39,8 @@ public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword =
Duration = TimeSpan.FromSeconds(10),
Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal,
Password = withPassword ? @"password" : null,
- PlaylistItemStats = ruleset == null
- ? null
- : new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] },
- Playlist = ruleset == null
- ? Array.Empty()
- : [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }]
+ PlaylistItemStats = new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] },
+ Playlist = [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }]
});
}
}
@@ -46,7 +48,11 @@ public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword =
public void AddRoom(Room room)
{
room.RoomID = -currentRoomId;
- api.Queue(new CreateRoomRequest(room));
+
+ var req = new CreateRoomRequest(room);
+ req.Success += AddOrUpdateRoom;
+ api.Queue(req);
+
currentRoomId++;
}
}
From 7c38089c7559350de5080cdad9b55d0e5165d41b Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 16:22:52 +0900
Subject: [PATCH 07/13] Rename methods
---
.../Online/Multiplayer/MultiplayerClient.cs | 37 +++++++------
.../Multiplayer/OnlineMultiplayerClient.cs | 54 +++++++++----------
.../Multiplayer/TestMultiplayerClient.cs | 6 +--
3 files changed, 50 insertions(+), 47 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index 7dfe974651ac..a8f314d37292 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -171,7 +171,7 @@ public async Task CreateRoom(Room room)
throw new InvalidOperationException("Cannot join a multiplayer room while already in one.");
var cancellationSource = joinCancellationSource = new CancellationTokenSource();
- await initRoom(room, r => CreateRoom(new MultiplayerRoom(room)), cancellationSource.Token).ConfigureAwait(false);
+ await initRoom(room, r => CreateRoomInternal(new MultiplayerRoom(room)), cancellationSource.Token).ConfigureAwait(false);
}
///
@@ -187,7 +187,7 @@ public async Task JoinRoom(Room room, string? password = null)
Debug.Assert(room.RoomID != null);
var cancellationSource = joinCancellationSource = new CancellationTokenSource();
- await initRoom(room, r => JoinRoom(room.RoomID.Value, password ?? room.Password), cancellationSource.Token).ConfigureAwait(false);
+ 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)
@@ -236,21 +236,6 @@ protected virtual void OnRoomJoined()
{
}
- ///
- /// Creates the with the given settings.
- ///
- /// The room.
- /// The joined
- protected abstract Task CreateRoom(MultiplayerRoom room);
-
- ///
- /// Joins the with a given ID.
- ///
- /// The room ID.
- /// An optional password to use when joining the room.
- /// The joined .
- protected abstract Task JoinRoom(long roomId, string? password = null);
-
public Task LeaveRoom()
{
if (Room == null)
@@ -279,6 +264,24 @@ public Task LeaveRoom()
});
}
+ ///
+ /// Creates the with the given settings.
+ ///
+ /// The room.
+ /// The joined
+ protected abstract Task CreateRoomInternal(MultiplayerRoom room);
+
+ ///
+ /// Joins the with a given ID.
+ ///
+ /// The room ID.
+ /// An optional password to use when joining the room.
+ /// The joined .
+ protected abstract Task JoinRoomInternal(long roomId, string? password = null);
+
+ ///
+ /// Leaves the currently-joined .
+ ///
protected abstract Task LeaveRoomInternal();
public abstract Task InvitePlayer(int userId);
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index 05f3e444054b..068ba277890e 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -75,7 +75,32 @@ private void load(IAPIProvider api)
}
}
- protected override async Task JoinRoom(long roomId, string? password = null)
+ protected override async Task CreateRoomInternal(MultiplayerRoom room)
+ {
+ if (!IsConnected.Value)
+ throw new OperationCanceledException();
+
+ Debug.Assert(connection != null);
+
+ try
+ {
+ return await connection.InvokeAsync(nameof(IMultiplayerServer.CreateRoom), room).ConfigureAwait(false);
+ }
+ catch (HubException exception)
+ {
+ if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
+ {
+ Debug.Assert(connector != null);
+
+ await connector.Reconnect().ConfigureAwait(false);
+ return await CreateRoomInternal(room).ConfigureAwait(false);
+ }
+
+ throw;
+ }
+ }
+
+ protected override async Task JoinRoomInternal(long roomId, string? password = null)
{
if (!IsConnected.Value)
throw new OperationCanceledException();
@@ -93,7 +118,7 @@ protected override async Task JoinRoom(long roomId, string? pas
Debug.Assert(connector != null);
await connector.Reconnect().ConfigureAwait(false);
- return await JoinRoom(roomId, password).ConfigureAwait(false);
+ return await JoinRoomInternal(roomId, password).ConfigureAwait(false);
}
throw;
@@ -266,31 +291,6 @@ public override Task RemovePlaylistItem(long playlistItemId)
return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId);
}
- protected override async Task CreateRoom(MultiplayerRoom room)
- {
- if (!IsConnected.Value)
- throw new OperationCanceledException();
-
- Debug.Assert(connection != null);
-
- try
- {
- return await connection.InvokeAsync(nameof(IMultiplayerServer.CreateRoom), room).ConfigureAwait(false);
- }
- catch (HubException exception)
- {
- if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
- {
- Debug.Assert(connector != null);
-
- await connector.Reconnect().ConfigureAwait(false);
- return await CreateRoom(room).ConfigureAwait(false);
- }
-
- throw;
- }
- }
-
public override Task DisconnectInternal()
{
if (connector == null)
diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
index d514fc0d7e7c..359b223ad2a2 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -208,7 +208,7 @@ public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBea
((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(clone(userId), clone(user.BeatmapAvailability));
}
- protected override async Task JoinRoom(long roomId, string? password = null)
+ protected override async Task JoinRoomInternal(long roomId, string? password = null)
{
if (RoomJoined || ServerAPIRoom != null)
throw new InvalidOperationException("Already joined a room");
@@ -485,7 +485,7 @@ public async Task RemoveUserPlaylistItem(int userId, long playlistItemId)
public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, clone(playlistItemId));
- protected override Task CreateRoom(MultiplayerRoom room)
+ protected override Task CreateRoomInternal(MultiplayerRoom room)
{
Room apiRoom = new Room(room)
{
@@ -495,7 +495,7 @@ protected override Task CreateRoom(MultiplayerRoom room)
};
AddServerSideRoom(apiRoom, api.LocalUser.Value);
- return JoinRoom(apiRoom.RoomID!.Value, room.Settings.Password);
+ return JoinRoomInternal(apiRoom.RoomID!.Value, room.Settings.Password);
}
private async Task changeMatchType(MatchType type)
From a198b0830affdab861037c0a90525946fa446b5d Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 17:18:01 +0900
Subject: [PATCH 08/13] Add comment indicating RoomManager shouldn't exist
---
osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index 3abb4098fb9f..a1b61ea7a335 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -13,6 +13,7 @@
namespace osu.Game.Screens.OnlinePlay.Components
{
+ // Todo: This class should be inlined into the lounge.
public partial class RoomManager : Component, IRoomManager
{
public event Action? RoomsUpdated;
From f2d8ea299777ad6168eb90d04a574d10bf083837 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 18:25:55 +0900
Subject: [PATCH 09/13] Fix incorrect continuation
---
.../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 279b140d36c0..72b581eac113 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -472,7 +472,7 @@ private void apply()
{
client.CreateRoom(room).ContinueWith(t => Schedule(() =>
{
- if (t.IsCompleted)
+ if (t.IsCompletedSuccessfully)
onSuccess(room);
else if (t.IsFaulted)
{
From 6dbf466009f6ab12f2613eebb970a2a1d1e101b3 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 18:30:11 +0900
Subject: [PATCH 10/13] Fix incorrect exception handling
In particular, when the exception is:
`AggregateException { AggregateException { HubException } }`,
then the existing code will only unwrap the first aggregate exception.
The overlay's code was copied from the extension so both have been
adjusted here.
---
.../Online/Multiplayer/MultiplayerClientExtensions.cs | 9 +++------
.../Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs | 8 ++------
2 files changed, 5 insertions(+), 12 deletions(-)
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
index d846e7f5669c..1cc5a8e70a80 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
+using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Logging;
namespace osu.Game.Online.Multiplayer
@@ -16,12 +17,8 @@ public static void FireAndForget(this Task task, Action? onSuccess = null, Actio
{
if (t.IsFaulted)
{
- Exception? exception = t.Exception;
-
- if (exception is AggregateException ae)
- exception = ae.InnerException;
-
- Debug.Assert(exception != null);
+ Debug.Assert(t.Exception != null);
+ Exception exception = t.Exception.AsSingular();
if (exception.GetHubExceptionMessage() is string message)
// Hub exceptions generally contain something we can show the user directly.
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 72b581eac113..2a5a83fadfa5 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -476,12 +476,8 @@ private void apply()
onSuccess(room);
else if (t.IsFaulted)
{
- Exception? exception = t.Exception;
-
- if (exception is AggregateException ae)
- exception = ae.InnerException;
-
- Debug.Assert(exception != null);
+ Debug.Assert(t.Exception != null);
+ Exception exception = t.Exception.AsSingular();
if (exception.GetHubExceptionMessage() is string message)
onError(message);
From e9d6411e615ba85a2989511a9f374682b20d25cf Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 23 Jan 2025 19:10:11 +0900
Subject: [PATCH 11/13] Clean up error handling
---
.../Match/MultiplayerMatchSettingsOverlay.cs | 58 +++++++++----------
1 file changed, 27 insertions(+), 31 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 2a5a83fadfa5..eda3bace40a7 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -463,9 +463,9 @@ private void apply()
.ContinueWith(t => Schedule(() =>
{
if (t.IsCompletedSuccessfully)
- onSuccess(room);
+ onSuccess();
else
- onError(t.Exception?.AsSingular().Message ?? "Error changing settings.");
+ onError(t.Exception, "Error changing settings");
}));
}
else
@@ -473,26 +473,16 @@ private void apply()
client.CreateRoom(room).ContinueWith(t => Schedule(() =>
{
if (t.IsCompletedSuccessfully)
- onSuccess(room);
- else if (t.IsFaulted)
- {
- Debug.Assert(t.Exception != null);
- Exception exception = t.Exception.AsSingular();
-
- if (exception.GetHubExceptionMessage() is string message)
- onError(message);
- else
- onError($"Error creating room: {exception}");
- }
+ onSuccess();
else
- onError("Error creating room.");
+ onError(t.Exception, "Error creating room");
}));
}
}
private void hideError() => ErrorText.FadeOut(50);
- private void onSuccess(Room room) => Schedule(() =>
+ private void onSuccess() => Schedule(() =>
{
Debug.Assert(applyingSettingsOperation != null);
@@ -502,28 +492,34 @@ private void onSuccess(Room room) => Schedule(() =>
applyingSettingsOperation = null;
});
- private void onError(string text) => Schedule(() =>
+ private void onError(Exception? exception, string description)
{
- Debug.Assert(applyingSettingsOperation != null);
+ if (exception is AggregateException aggregateException)
+ exception = aggregateException.AsSingular();
- // see https://github.com/ppy/osu-web/blob/2c97aaeb64fb4ed97c747d8383a35b30f57428c7/app/Models/Multiplayer/PlaylistItem.php#L48.
- const string not_found_prefix = "beatmaps not found:";
+ string message = exception?.GetHubExceptionMessage() ?? $"{description} ({exception?.Message})";
- if (text.StartsWith(not_found_prefix, StringComparison.Ordinal))
+ Schedule(() =>
{
- ErrorText.Text = "The selected beatmap is not available online.";
- room.Playlist.SingleOrDefault()?.MarkInvalid();
- }
- else
- {
- ErrorText.Text = text;
- }
+ Debug.Assert(applyingSettingsOperation != null);
- ErrorText.FadeIn(50);
+ // see https://github.com/ppy/osu-web/blob/2c97aaeb64fb4ed97c747d8383a35b30f57428c7/app/Models/Multiplayer/PlaylistItem.php#L48.
+ const string not_found_prefix = "beatmaps not found:";
- applyingSettingsOperation.Dispose();
- applyingSettingsOperation = null;
- });
+ if (message.StartsWith(not_found_prefix, StringComparison.Ordinal))
+ {
+ ErrorText.Text = "The selected beatmap is not available online.";
+ room.Playlist.SingleOrDefault()?.MarkInvalid();
+ }
+ else
+ ErrorText.Text = message;
+
+ ErrorText.FadeIn(50);
+
+ applyingSettingsOperation.Dispose();
+ applyingSettingsOperation = null;
+ });
+ }
protected override void Dispose(bool isDisposing)
{
From ab4162e2aafc4e246ba070870e4967ab7a6e00cb Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Sat, 25 Jan 2025 19:27:21 +0900
Subject: [PATCH 12/13] Various refactorings and cleanups
---
.../TestSceneMultiplayerLoungeSubScreen.cs | 28 +++++--------------
.../TestScenePlaylistsLoungeSubScreen.cs | 28 +++----------------
.../Multiplayer/IMultiplayerLoungeServer.cs | 5 ++++
.../Online/Multiplayer/MultiplayerClient.cs | 3 +-
osu.Game/Online/Rooms/CreateRoomRequest.cs | 2 ++
osu.Game/Online/Rooms/JoinRoomRequest.cs | 2 ++
.../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +-
.../OnlinePlay/Lounge/IOnlinePlayLounge.cs | 6 ++--
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 6 ++--
.../Screens/OnlinePlay/Match/RoomSubScreen.cs | 3 ++
.../Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +-
.../Playlists/PlaylistsLoungeSubScreen.cs | 2 +-
12 files changed, 34 insertions(+), 55 deletions(-)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index 4a259149e254..eb649acd2d2a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -40,7 +40,7 @@ public override void SetUpSteps()
[Test]
public void TestJoinRoomWithoutPassword()
{
- addRoom(false);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
@@ -50,7 +50,7 @@ public void TestJoinRoomWithoutPassword()
[Test]
public void TestPopoverHidesOnBackButton()
{
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
@@ -70,7 +70,7 @@ public void TestPopoverHidesOnBackButton()
[Test]
public void TestPopoverHidesOnLeavingScreen()
{
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
@@ -86,7 +86,7 @@ public void TestJoinRoomWithIncorrectPasswordViaButton()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
@@ -105,7 +105,7 @@ public void TestJoinRoomWithIncorrectPasswordViaEnter()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
@@ -124,7 +124,7 @@ public void TestJoinRoomWithCorrectPassword()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
@@ -139,7 +139,7 @@ public void TestJoinRoomWithPasswordViaKeyboardOnly()
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- addRoom(true);
+ AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
@@ -149,20 +149,6 @@ public void TestJoinRoomWithPasswordViaKeyboardOnly()
AddAssert("room joined", () => MultiplayerClient.RoomJoined);
}
- private void addRoom(bool withPassword)
- {
- int initialRoomCount = 0;
-
- AddStep("add room", () =>
- {
- initialRoomCount = roomsContainer.Rooms.Count;
- RoomManager.AddRooms(1, withPassword: withPassword);
- loungeScreen.RefreshRooms();
- });
-
- AddUntilStep("wait for room to appear", () => roomsContainer.Rooms.Count == initialRoomCount + 1);
- }
-
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
}
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index 0897a3b2f568..53c7873de52a 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -35,12 +35,7 @@ public override void SetUpSteps()
[Test]
public void TestManyRooms()
{
- AddStep("add rooms", () =>
- {
- RoomManager.AddRooms(500);
- loungeScreen.RefreshRooms();
- });
-
+ AddStep("add rooms", () => RoomManager.AddRooms(500));
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 500);
}
@@ -49,12 +44,7 @@ public void TestScrollByDraggingRooms()
{
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
- AddStep("add rooms", () =>
- {
- RoomManager.AddRooms(30);
- loungeScreen.RefreshRooms();
- });
-
+ AddStep("add rooms", () => RoomManager.AddRooms(30));
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -71,12 +61,7 @@ public void TestScrollByDraggingRooms()
[Test]
public void TestScrollSelectedIntoView()
{
- AddStep("add rooms", () =>
- {
- RoomManager.AddRooms(30);
- loungeScreen.RefreshRooms();
- });
-
+ AddStep("add rooms", () => RoomManager.AddRooms(30));
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -90,12 +75,7 @@ public void TestScrollSelectedIntoView()
[Test]
public void TestEnteringRoomTakesLeaseOnSelection()
{
- AddStep("add rooms", () =>
- {
- RoomManager.AddRooms(1);
- loungeScreen.RefreshRooms();
- });
-
+ AddStep("add rooms", () => RoomManager.AddRooms(1));
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 1);
AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
diff --git a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
index c5eb6f9b36c6..0ee9fa54cd81 100644
--- a/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
+++ b/osu.Game/Online/Multiplayer/IMultiplayerLoungeServer.cs
@@ -10,6 +10,11 @@ namespace osu.Game.Online.Multiplayer
///
public interface IMultiplayerLoungeServer
{
+ ///
+ /// Request to create a multiplayer room.
+ ///
+ /// The room to create.
+ /// The created multiplayer room.
Task CreateRoom(MultiplayerRoom room);
///
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index a8f314d37292..6749ed953574 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -168,7 +168,7 @@ private void load()
public async Task CreateRoom(Room room)
{
if (Room != null)
- throw new InvalidOperationException("Cannot join a multiplayer room while already in one.");
+ 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);
@@ -212,6 +212,7 @@ await runOnUpdateThreadAsync(() =>
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);
diff --git a/osu.Game/Online/Rooms/CreateRoomRequest.cs b/osu.Game/Online/Rooms/CreateRoomRequest.cs
index 9773bb5e7d97..5b2ea77aad66 100644
--- a/osu.Game/Online/Rooms/CreateRoomRequest.cs
+++ b/osu.Game/Online/Rooms/CreateRoomRequest.cs
@@ -15,6 +15,8 @@ public class CreateRoomRequest : APIRequest
public CreateRoomRequest(Room room)
{
Room = room;
+
+ // Also copy back to the source model, since it is likely to have been stored elsewhere.
Success += r => Room.CopyFrom(r);
}
diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs
index 13e7ac8c84a6..610e88724214 100644
--- a/osu.Game/Online/Rooms/JoinRoomRequest.cs
+++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs
@@ -16,6 +16,8 @@ public JoinRoomRequest(Room room, string? password)
{
Room = room;
Password = password;
+
+ // Also copy back to the source model, since it is likely to have been stored elsewhere.
Success += r => Room.CopyFrom(r);
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
index 032a231ad3f7..5de35ef10145 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
@@ -162,7 +162,7 @@ public MenuItem[] ContextMenuItems
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
{
- lounge?.Clone(Room);
+ lounge?.OpenCopy(Room);
})
};
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs b/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
index 8fa7d0751f0f..73ab84af13b3 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
@@ -18,10 +18,10 @@ public interface IOnlinePlayLounge
void Join(Room room, string? password, Action? onSuccess = null, Action? onFailure = null);
///
- /// Clones the given room and opens it as a fresh (not-yet-created) one.
+ /// Copies the given room and opens it as a fresh (not-yet-created) one.
///
- /// The room to clone.
- void Clone(Room room);
+ /// The room to copy.
+ void OpenCopy(Room room);
///
/// Closes the given room.
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index df17063fdf79..0e08e398a494 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -309,7 +309,7 @@ public void Join(Room room, string? password, Action? onSuccess = null, Ac
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
- TryJoin(room, password, r =>
+ JoinInternal(room, password, r =>
{
Open(room);
joiningRoomOperation?.Dispose();
@@ -323,9 +323,9 @@ public void Join(Room room, string? password, Action? onSuccess = null, Ac
});
});
- protected abstract void TryJoin(Room room, string? password, Action onSuccess, Action onFailure);
+ protected abstract void JoinInternal(Room room, string? password, Action onSuccess, Action onFailure);
- public void Clone(Room room)
+ public void OpenCopy(Room room)
{
Debug.Assert(room.RoomID != null);
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index d37f3b877c26..80b3961f4413 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -353,6 +353,9 @@ public override bool OnExiting(ScreenExitEvent e)
return base.OnExiting(e);
}
+ ///
+ /// Parts from the current room.
+ ///
protected abstract void PartRoom();
private bool ensureExitConfirmed()
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index e901ecbdce51..873a9cde8896 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -81,7 +81,7 @@ protected override FilterCriteria CreateFilterCriteria()
protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
- protected override void TryJoin(Room room, string? password, Action onSuccess, Action onFailure)
+ protected override void JoinInternal(Room room, string? password, Action onSuccess, Action onFailure)
{
client.JoinRoom(room, password).ContinueWith(result =>
{
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index 92415e0eb180..6ed367328c1d 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -60,7 +60,7 @@ protected override FilterCriteria CreateFilterCriteria()
return criteria;
}
- protected override void TryJoin(Room room, string? password, Action onSuccess, Action onFailure)
+ protected override void JoinInternal(Room room, string? password, Action onSuccess, Action onFailure)
{
var joinRoomRequest = new JoinRoomRequest(room, password);
From 8c85616d1c8677a859bf007291997b092786f94c Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 11 Feb 2025 21:28:21 +0900
Subject: [PATCH 13/13] Fix test
---
osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs
index 66c465cbedfa..bd1e15d06d29 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs
@@ -32,7 +32,7 @@ public void TestBasics()
Bindable playingState = new Bindable();
GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), healthProcessor: new OsuHealthProcessor(0), localUserPlayingState: playingState);
TestSpectatorClient spectatorClient = new TestSpectatorClient();
- TestMultiplayerClient multiplayerClient = new TestMultiplayerClient(new TestMultiplayerRoomManager(new TestRoomRequestsHandler()));
+ TestMultiplayerClient multiplayerClient = new TestMultiplayerClient(new TestRoomRequestsHandler());
AddStep("create spectator list", () =>
{