From 2417d4de8302f7682bb5703ead03936c336fd33d Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Sun, 24 Nov 2024 21:46:33 -0500
Subject: [PATCH 001/349] Add `OsuOnlineStore` for proxying external media
lookups
---
osu.Game/Audio/PreviewTrackManager.cs | 7 ++++---
osu.Game/Online/OsuOnlineStore.cs | 29 +++++++++++++++++++++++++++
osu.Game/OsuGame.cs | 3 +++
osu.Game/OsuGameBase.cs | 2 +-
4 files changed, 37 insertions(+), 4 deletions(-)
create mode 100644 osu.Game/Online/OsuOnlineStore.cs
diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs
index 1d710e6395..81564cc2e8 100644
--- a/osu.Game/Audio/PreviewTrackManager.cs
+++ b/osu.Game/Audio/PreviewTrackManager.cs
@@ -6,9 +6,10 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
+using osu.Game.Online;
+using osu.Game.Online.API;
namespace osu.Game.Audio
{
@@ -28,9 +29,9 @@ namespace osu.Game.Audio
}
[BackgroundDependencyLoader]
- private void load(AudioManager audioManager)
+ private void load(AudioManager audioManager, IAPIProvider api)
{
- trackStore = audioManager.GetTrackStore(new OnlineStore());
+ trackStore = audioManager.GetTrackStore(new OsuOnlineStore(api.APIEndpointUrl));
}
///
diff --git a/osu.Game/Online/OsuOnlineStore.cs b/osu.Game/Online/OsuOnlineStore.cs
new file mode 100644
index 0000000000..bb69338b01
--- /dev/null
+++ b/osu.Game/Online/OsuOnlineStore.cs
@@ -0,0 +1,29 @@
+// 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.Framework.IO.Stores;
+
+namespace osu.Game.Online
+{
+ ///
+ /// An which proxies external media lookups through osu-web.
+ ///
+ public class OsuOnlineStore : OnlineStore
+ {
+ private readonly string apiEndpointUrl;
+
+ public OsuOnlineStore(string apiEndpointUrl)
+ {
+ this.apiEndpointUrl = apiEndpointUrl;
+ }
+
+ protected override string GetLookupUrl(string url)
+ {
+ if (Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) && uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase))
+ return url;
+
+ return $@"{apiEndpointUrl}/beatmapsets/discussions/media-url?url={url}";
+ }
+ }
+}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index dce24c6ee7..1fe41baf2f 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -29,6 +29,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.IO.Stores;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -819,6 +820,8 @@ namespace osu.Game
protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything);
+ protected override OnlineStore CreateOnlineStore() => new OsuOnlineStore(CreateEndpoints().APIEndpointUrl);
+
#region Beatmap progression
private void beatmapChanged(ValueChangedEvent beatmap)
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index dc13924b4f..d231238699 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -279,7 +279,7 @@ namespace osu.Game
dependencies.CacheAs(Storage);
var largeStore = new LargeTextureStore(Host.Renderer, Host.CreateTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")));
- largeStore.AddTextureSource(Host.CreateTextureLoaderStore(new OnlineStore()));
+ largeStore.AddTextureSource(Host.CreateTextureLoaderStore(CreateOnlineStore()));
dependencies.Cache(largeStore);
dependencies.CacheAs(LocalConfig);
From 2a7133d6d3dc3bdb5a2cafddde7fc2df834b23e1 Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Sun, 24 Nov 2024 21:47:10 -0500
Subject: [PATCH 002/349] Add test scene using `OsuOnlineStore` to test lookups
---
.../Visual/Online/TestSceneMediaProxying.cs | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs
diff --git a/osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs b/osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs
new file mode 100644
index 0000000000..868bfa6cc4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs
@@ -0,0 +1,63 @@
+// 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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Platform;
+using osu.Game.Graphics.Containers.Markdown;
+using osu.Game.Online;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public partial class TestSceneMediaProxying : OsuTestScene
+ {
+ [Resolved]
+ private GameHost host { get; set; } = null!;
+
+ [Test]
+ public void TestExternalImageLink()
+ {
+ AddStep("load image", () => setup(new OsuMarkdownContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Text = "",
+ }));
+ }
+
+ [Test]
+ public void TestLocalImageLink()
+ {
+ AddStep("load image", () => setup(new OsuMarkdownContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Text = "",
+ }));
+ }
+
+ [Test]
+ public void TestInvalidImageLink()
+ {
+ AddStep("load image", () => setup(new OsuMarkdownContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Text = "",
+ }));
+ }
+
+ private void setup(Drawable drawable)
+ {
+ var onlineStore = new OsuOnlineStore(@"https://osu.ppy.sh");
+ var textureStore = new TextureStore(host.Renderer, host.CreateTextureLoaderStore(onlineStore));
+
+ Child = new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[] { (typeof(TextureStore), textureStore) },
+ Child = drawable,
+ };
+ }
+ }
+}
From 9a89d402b9d6e5591a4c67668203ee0d53f27ba3 Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Mon, 25 Nov 2024 00:39:32 -0500
Subject: [PATCH 003/349] Perform proxying only on osu! markdown images
---
.../Graphics/Containers/Markdown/OsuMarkdownImage.cs | 3 +++
osu.Game/Online/OsuOnlineStore.cs | 11 ++++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
index 10207dd389..a36bbf4f6f 100644
--- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
+++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
@@ -17,5 +17,8 @@ namespace osu.Game.Graphics.Containers.Markdown
{
TooltipText = linkInline.Title;
}
+
+ protected override ImageContainer CreateImageContainer(string url)
+ => base.CreateImageContainer($@"https://osu.ppy.sh/beatmapsets/discussions/media-url?url={url}");
}
}
diff --git a/osu.Game/Online/OsuOnlineStore.cs b/osu.Game/Online/OsuOnlineStore.cs
index bb69338b01..dddd453faf 100644
--- a/osu.Game/Online/OsuOnlineStore.cs
+++ b/osu.Game/Online/OsuOnlineStore.cs
@@ -3,12 +3,10 @@
using System;
using osu.Framework.IO.Stores;
+using osu.Framework.Logging;
namespace osu.Game.Online
{
- ///
- /// An which proxies external media lookups through osu-web.
- ///
public class OsuOnlineStore : OnlineStore
{
private readonly string apiEndpointUrl;
@@ -20,8 +18,11 @@ namespace osu.Game.Online
protected override string GetLookupUrl(string url)
{
- if (Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) && uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase))
- return url;
+ if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) || !uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase))
+ {
+ Logger.Log($@"Blocking resource lookup from external website: {url}", LoggingTarget.Network, LogLevel.Important);
+ return string.Empty;
+ }
return $@"{apiEndpointUrl}/beatmapsets/discussions/media-url?url={url}";
}
From 83f8fa7472175d053172459ac64ab9469832733d Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Mon, 25 Nov 2024 00:41:40 -0500
Subject: [PATCH 004/349] Update test scene
---
...aProxying.cs => TestSceneImageProxying.cs} | 27 +++++--------------
1 file changed, 7 insertions(+), 20 deletions(-)
rename osu.Game.Tests/Visual/Online/{TestSceneMediaProxying.cs => TestSceneImageProxying.cs} (59%)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
similarity index 59%
rename from osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs
rename to osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
index 868bfa6cc4..696073c10d 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneMediaProxying.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
@@ -12,7 +12,7 @@ using osu.Game.Online;
namespace osu.Game.Tests.Visual.Online
{
- public partial class TestSceneMediaProxying : OsuTestScene
+ public partial class TestSceneImageProxying : OsuTestScene
{
[Resolved]
private GameHost host { get; set; } = null!;
@@ -20,44 +20,31 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestExternalImageLink()
{
- AddStep("load image", () => setup(new OsuMarkdownContainer
+ AddStep("load image", () => Child = new OsuMarkdownContainer
{
RelativeSizeAxes = Axes.Both,
Text = "",
- }));
+ });
}
[Test]
public void TestLocalImageLink()
{
- AddStep("load image", () => setup(new OsuMarkdownContainer
+ AddStep("load image", () => Child = new OsuMarkdownContainer
{
RelativeSizeAxes = Axes.Both,
Text = "",
- }));
+ });
}
[Test]
public void TestInvalidImageLink()
{
- AddStep("load image", () => setup(new OsuMarkdownContainer
+ AddStep("load image", () => Child = new OsuMarkdownContainer
{
RelativeSizeAxes = Axes.Both,
Text = "",
- }));
- }
-
- private void setup(Drawable drawable)
- {
- var onlineStore = new OsuOnlineStore(@"https://osu.ppy.sh");
- var textureStore = new TextureStore(host.Renderer, host.CreateTextureLoaderStore(onlineStore));
-
- Child = new DependencyProvidingContainer
- {
- RelativeSizeAxes = Axes.Both,
- CachedDependencies = new (Type, object)[] { (typeof(TextureStore), textureStore) },
- Child = drawable,
- };
+ });
}
}
}
From dbe2741982ed3741ef527d79d7e37eea6ff7766b Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Thu, 28 Nov 2024 22:19:44 -0500
Subject: [PATCH 005/349] Update specified endpoint
---
osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
index a36bbf4f6f..ff7df18f00 100644
--- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
+++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
@@ -19,6 +19,6 @@ namespace osu.Game.Graphics.Containers.Markdown
}
protected override ImageContainer CreateImageContainer(string url)
- => base.CreateImageContainer($@"https://osu.ppy.sh/beatmapsets/discussions/media-url?url={url}");
+ => base.CreateImageContainer($@"https://osu.ppy.sh/media-url?url={url}");
}
}
From 9a4c419c568764fabd2d7624d288966c84986f5c Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Sat, 30 Nov 2024 22:37:05 -0500
Subject: [PATCH 006/349] Remove unnecessary usage of link proxying in
`OsuOnlineStore`
Links are checked to be in the ppy.sh domain here.
---
osu.Game/Online/OsuOnlineStore.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/osu.Game/Online/OsuOnlineStore.cs b/osu.Game/Online/OsuOnlineStore.cs
index dddd453faf..c3e81c503f 100644
--- a/osu.Game/Online/OsuOnlineStore.cs
+++ b/osu.Game/Online/OsuOnlineStore.cs
@@ -18,13 +18,14 @@ namespace osu.Game.Online
protected override string GetLookupUrl(string url)
{
+ // add leading dot to avoid matching hosts named "ppy.sh"
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) || !uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase))
{
Logger.Log($@"Blocking resource lookup from external website: {url}", LoggingTarget.Network, LogLevel.Important);
return string.Empty;
}
- return $@"{apiEndpointUrl}/beatmapsets/discussions/media-url?url={url}";
+ return url;
}
}
}
From ee369ef86d54c6f1359742edc438237e01b718e3 Mon Sep 17 00:00:00 2001
From: Salman Alshamrani
Date: Sat, 30 Nov 2024 22:38:43 -0500
Subject: [PATCH 007/349] Remove unused using directives
---
osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs | 3 ---
1 file changed, 3 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
index 696073c10d..0cf6fec6f0 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneImageProxying.cs
@@ -1,14 +1,11 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Game.Graphics.Containers.Markdown;
-using osu.Game.Online;
namespace osu.Game.Tests.Visual.Online
{
From ad422295c85d257044edd33dba7284b7c8d9b631 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 10 Jan 2025 21:38:37 +0900
Subject: [PATCH 008/349] 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 f8660a656e..7647134646 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,6 +342,29 @@ namespace osu.Game.Online.Rooms
// 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 009/349] 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 8be703e620..6e467c1d26 100644
--- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
+++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs
@@ -60,5 +60,20 @@ namespace osu.Game.Online.Rooms
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 010/349] Create multiplayer rooms via multiplayer server
---
.../Multiplayer/IMultiplayerLoungeServer.cs | 2 +
.../Online/Multiplayer/MultiplayerClient.cs | 42 ++++++++++++------
.../Online/Multiplayer/MultiplayerRoom.cs | 9 ++++
.../Multiplayer/MultiplayerRoomSettings.cs | 14 ++++++
.../Multiplayer/OnlineMultiplayerClient.cs | 25 +++++++++++
osu.Game/Online/Rooms/Room.cs | 23 ----------
.../Match/MultiplayerMatchSettingsOverlay.cs | 44 +++++++++----------
.../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 f266c38b8b..c5eb6f9b36 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 4a28124583..d0c3a1fa06 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -165,6 +165,15 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
if (Room != null)
throw new InvalidOperationException("Cannot join a multiplayer room while already in one.");
- var cancellationSource = joinCancellationSource = new CancellationTokenSource();
+ 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 @@ namespace osu.Game.Online.Multiplayer
postServerShuttingDownNotification();
OnRoomJoined();
- }, cancellationSource.Token).ConfigureAwait(false);
- }, cancellationSource.Token).ConfigureAwait(false);
+ }, cancellationToken).ConfigureAwait(false);
+ }, cancellationToken).ConfigureAwait(false);
}
///
@@ -227,6 +236,13 @@ namespace osu.Game.Online.Multiplayer
{
}
+ ///
+ /// 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 00048fa931..f7bd4490ff 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 @@ namespace osu.Game.Online.Multiplayer
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 c73b02874e..c264ec1eef 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs
@@ -35,6 +35,20 @@ namespace osu.Game.Online.Multiplayer
[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 40436d730e..524873ef66 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -266,6 +266,31 @@ namespace osu.Game.Online.Multiplayer
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 7647134646..f8660a656e 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,29 +342,6 @@ namespace osu.Game.Online.Rooms
// 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 1372054149..279b140d36 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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
SettingsApplied = Hide,
- SelectedItem = { BindTarget = SelectedItem }
};
protected partial class MatchSettings : CompositeDrawable
@@ -65,7 +58,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
[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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
RelativeSizeAxes = Axes.X,
Height = DrawableRoomPlaylistItem.HEIGHT,
- SelectedItem = { BindTarget = SelectedItem }
},
selectBeatmapButton = new RoundedButton
{
@@ -482,19 +470,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
}
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;
+ client.CreateRoom(room).ContinueWith(t => Schedule(() =>
+ {
+ if (t.IsCompleted)
+ onSuccess(room);
+ else if (t.IsFaulted)
+ {
+ Exception? exception = t.Exception;
- if (int.TryParse(MaxParticipantsField.Text, out int max))
- room.MaxParticipants = max;
- else
- room.MaxParticipants = null;
+ 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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
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 edc45dbf7c..06ea5ee033 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -233,10 +233,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 4d812abf11..70e298f3e0 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -483,6 +483,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 011/349] 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 d0c3a1fa06..e5eade8c1d 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -171,7 +171,7 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
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 524873ef66..05f3e44405 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -275,7 +275,7 @@ namespace osu.Game.Online.Multiplayer
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 012/349] 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 9951f62c77..d06a91433d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -8,7 +8,6 @@ using osu.Framework.Graphics.UserInterface;
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 e5eade8c1d..7dfe974651 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -253,6 +253,9 @@ namespace osu.Game.Online.Multiplayer
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 63a3b7bfa8..9773bb5e7d 100644
--- a/osu.Game/Online/Rooms/CreateRoomRequest.cs
+++ b/osu.Game/Online/Rooms/CreateRoomRequest.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Online.Rooms
public CreateRoomRequest(Room room)
{
Room = room;
+ Success += r => Room.CopyFrom(r);
}
protected override WebRequest CreateWebRequest()
@@ -23,7 +24,6 @@ namespace osu.Game.Online.Rooms
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 dfc7a53fb2..13e7ac8c84 100644
--- a/osu.Game/Online/Rooms/JoinRoomRequest.cs
+++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs
@@ -16,6 +16,7 @@ namespace osu.Game.Online.Rooms
{
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 73f980f0a3..3abb4098fb 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -5,12 +5,10 @@ using System;
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 @@ namespace osu.Game.Screens.OnlinePlay.Components
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 13a282dd52..e3d6d42c05 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;
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 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
[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 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{
this.room = room;
playlistItem = room.Playlist.Single();
- roomManager = new RoomManager();
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
}
@@ -131,7 +126,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- roomManager,
beatmapAvailabilityTracker,
new ScreenStack(new RoomBackgroundScreen(playlistItem))
{
@@ -426,7 +420,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
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 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
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 ed4fb7b15e..8ecb1dd7e0 100644
--- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
@@ -38,27 +38,5 @@ namespace osu.Game.Screens.OnlinePlay
/// 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 f00cf7427c..f3f4df166a 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -263,6 +263,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
});
});
+ 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 4ef31c02c3..d37f3b877c 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -343,7 +343,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (!ensureExitConfirmed())
return true;
- RoomManager?.PartRoom();
+ if (Room.RoomID != null)
+ PartRoom();
+
Mods.Value = Array.Empty();
onLeaving();
@@ -351,6 +353,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
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 bf316bb3da..dfed32aebc 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs
@@ -8,7 +8,6 @@ using osu.Framework.Logging;
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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 dd61caa3db..e901ecbdce 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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 06ea5ee033..553c0c9182 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -278,6 +278,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 7f09c9cbe9..0000000000
--- 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 17fb667e14..16462b90c1 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -36,12 +36,12 @@ namespace osu.Game.Screens.OnlinePlay
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 @@ namespace osu.Game.Screens.OnlinePlay
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 @@ namespace osu.Game.Screens.OnlinePlay
{
screenStack,
new Header(ScreenTitle, screenStack),
- RoomManager,
+ roomManager,
ongoingOperationTracker,
}
};
@@ -165,8 +163,6 @@ namespace osu.Game.Screens.OnlinePlay
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 fa1ee004c9..9b35a794a3 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 @@ namespace osu.Game.Screens.OnlinePlay
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 d66b4f844c..92415e0eb1 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 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
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 88af161cc8..b3d1d577ed 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs
@@ -75,9 +75,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private PurpleRoundedButton editPlaylistButton = null!;
- [Resolved]
- private IRoomManager? manager { get; set; }
-
[Resolved]
private IAPIProvider api { get; set; } = null!;
@@ -449,7 +446,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
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 9b4630ac0b..064c355a69 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -290,6 +290,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}));
}
+ 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 42cf317829..dca1fc8f3c 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 b998a638e5..59ac9a9749 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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 b1e3eafacc..60d169a46f 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 @@ namespace osu.Game.Tests.Visual.OnlinePlay
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 013/349] 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 559db16751..be30e06ed4 100644
--- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
+++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
@@ -8,7 +8,6 @@ using osu.Framework.Extensions.ObjectExtensions;
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 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
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 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
});
};
- 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 c5fb52461a..459a90d096 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 fb653cea8b..0966c61a3a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -58,7 +58,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Playlist =
@@ -286,7 +285,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Playlist =
@@ -336,7 +335,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
Password = "password",
@@ -789,7 +788,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create room", () =>
{
- roomManager.AddServerSideRoom(new Room
+ multiplayerClient.AddServerSideRoom(new Room
{
Name = "Test Room",
QueueMode = QueueMode.AllPlayers,
@@ -810,8 +809,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 d06a91433d..4a259149e2 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -9,18 +9,26 @@ using osu.Framework.Screens;
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 36f5bba384..77b75f407b 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
@@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 8c8dc8d69a..0897a3b2f5 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -35,7 +35,13 @@ namespace osu.Game.Tests.Visual.Playlists
[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 @@ namespace osu.Game.Tests.Visual.Playlists
{
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 @@ namespace osu.Game.Tests.Visual.Playlists
[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 @@ namespace osu.Game.Tests.Visual.Playlists
[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 5868331451..51e39e1b7f 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 @@ namespace osu.Game.Tests.Visual.Playlists
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 @@ namespace osu.Game.Tests.Visual.Playlists
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 @@ namespace osu.Game.Tests.Visual.Playlists
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
- RoomManager.CreateRequested = _ => errorMessage;
+ handleRequest = _ => errorMessage;
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
@@ -128,7 +147,7 @@ namespace osu.Game.Tests.Visual.Playlists
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 @@ namespace osu.Game.Tests.Visual.Playlists
{
}
}
-
- 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 1814fb70c8..e385ff3a03 100644
--- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
+++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs
@@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
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 @@ namespace osu.Game.Tests.Visual
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 @@ namespace osu.Game.Tests.Visual
}
};
- 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 @@ namespace osu.Game.Tests.Visual
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 f8660a656e..c5e292a19d 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -342,6 +342,23 @@ namespace osu.Game.Online.Rooms
// 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 0a55472c2d..032a231ad3 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.UserInterface;
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
}
[Resolved(canBeNull: true)]
- private LoungeSubScreen? lounge { get; set; }
+ private IOnlinePlayLounge? lounge { get; set; }
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
@@ -163,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
{
- lounge?.OpenCopy(Room);
+ lounge?.Clone(Room);
})
};
@@ -171,12 +170,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 0000000000..8fa7d0751f
--- /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 f3f4df166a..df17063fdf 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.Containers;
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 @@ using osuTK;
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 16462b90c1..8988c82dee 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -220,8 +220,6 @@ namespace osu.Game.Screens.OnlinePlay
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 efd0b80ebf..262816ae89 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 @@ namespace osu.Game.Tests.Visual.Multiplayer
///
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 dca1fc8f3c..d1497d5142 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -17,7 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 88202d4327..24c33f2f49 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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 70e298f3e0..d514fc0d7e 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -10,6 +10,7 @@ using MessagePack;
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;
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 59ac9a9749..0000000000
--- 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 e2670c9ad8..203922c057 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
RequestsHandler = new TestRoomRequestsHandler();
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
- RoomManager = CreateRoomManager();
+ RoomManager = new TestRoomManager();
UserLookupCache = new TestUserLookupCache();
BeatmapLookupCache = new BeatmapLookupCache();
@@ -80,7 +80,5 @@ namespace osu.Game.Tests.Visual.OnlinePlay
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 60d169a46f..bff2753929 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
@@ -22,8 +22,14 @@ namespace osu.Game.Tests.Visual.OnlinePlay
[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 @@ namespace osu.Game.Tests.Visual.OnlinePlay
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 @@ namespace osu.Game.Tests.Visual.OnlinePlay
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 014/349] 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 7dfe974651..a8f314d372 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -171,7 +171,7 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
{
}
- ///
- /// 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 @@ namespace osu.Game.Online.Multiplayer
});
}
+ ///
+ /// 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 05f3e44405..068ba27789 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -75,7 +75,32 @@ namespace osu.Game.Online.Multiplayer
}
}
- 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 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
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 d514fc0d7e..359b223ad2 100644
--- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs
@@ -208,7 +208,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
((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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
};
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 015/349] 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 3abb4098fb..a1b61ea7a3 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -13,6 +13,7 @@ using osu.Game.Online.Rooms;
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 016/349] 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 279b140d36..72b581eac1 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -472,7 +472,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
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 017/349] 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 d846e7f566..1cc5a8e70a 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClientExtensions.cs
@@ -5,6 +5,7 @@ using System;
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 @@ namespace osu.Game.Online.Multiplayer
{
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 72b581eac1..2a5a83fadf 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -476,12 +476,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
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 018/349] 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 2a5a83fadf..eda3bace40 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -463,9 +463,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
.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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
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 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
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 019/349] 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 4a259149e2..eb649acd2d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
[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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
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 @@ namespace osu.Game.Tests.Visual.Multiplayer
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 0897a3b2f5..53c7873de5 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -35,12 +35,7 @@ namespace osu.Game.Tests.Visual.Playlists
[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 @@ namespace osu.Game.Tests.Visual.Playlists
{
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 @@ namespace osu.Game.Tests.Visual.Playlists
[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 @@ namespace osu.Game.Tests.Visual.Playlists
[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 c5eb6f9b36..0ee9fa54cd 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 a8f314d372..6749ed9535 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -168,7 +168,7 @@ namespace osu.Game.Online.Multiplayer
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 @@ namespace osu.Game.Online.Multiplayer
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 9773bb5e7d..5b2ea77aad 100644
--- a/osu.Game/Online/Rooms/CreateRoomRequest.cs
+++ b/osu.Game/Online/Rooms/CreateRoomRequest.cs
@@ -15,6 +15,8 @@ namespace osu.Game.Online.Rooms
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 13e7ac8c84..610e887242 100644
--- a/osu.Game/Online/Rooms/JoinRoomRequest.cs
+++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs
@@ -16,6 +16,8 @@ namespace osu.Game.Online.Rooms
{
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 032a231ad3..5de35ef101 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
@@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
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 8fa7d0751f..73ab84af13 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/IOnlinePlayLounge.cs
@@ -18,10 +18,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
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 df17063fdf..0e08e398a4 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -309,7 +309,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
- TryJoin(room, password, r =>
+ JoinInternal(room, password, r =>
{
Open(room);
joiningRoomOperation?.Dispose();
@@ -323,9 +323,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
});
});
- 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 d37f3b877c..80b3961f44 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -353,6 +353,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
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 e901ecbdce..873a9cde88 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -81,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
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 92415e0eb1..6ed367328c 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
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 020/349] 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 66c465cbed..bd1e15d06d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorList.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
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", () =>
{
From 068a66e7d4c9bef92ea39e5237b37bc628e9e14f Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 18:35:35 +0900
Subject: [PATCH 021/349] Move room tracking to lounge subscreen
---
.../TestSceneLoungeRoomsContainer.cs | 28 ++++-----
.../TestScenePlaylistsLoungeSubScreen.cs | 30 ++++-----
.../Components/ListingPollingComponent.cs | 38 ++----------
.../Components/SelectionPollingComponent.cs | 5 +-
.../Lounge/Components/RoomsContainer.cs | 45 ++++----------
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 62 ++++++++++++++-----
.../Multiplayer/MultiplayerLoungeSubScreen.cs | 34 ----------
.../Playlists/PlaylistsLoungeSubScreen.cs | 3 -
8 files changed, 95 insertions(+), 150 deletions(-)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 797b69ec72..10df77f88c 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -50,17 +50,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add rooms", () => RoomManager.AddRooms(5, withSpotlightRooms: true));
- AddAssert("has 5 rooms", () => container.Rooms.Count == 5);
+ AddAssert("has 5 rooms", () => container.DrawableRooms.Count == 5);
- AddAssert("all spotlights at top", () => container.Rooms
+ AddAssert("all spotlights at top", () => container.DrawableRooms
.SkipWhile(r => r.Room.Category == RoomCategory.Spotlight)
.All(r => r.Room.Category == RoomCategory.Normal));
AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0)));
- AddAssert("has 4 rooms", () => container.Rooms.Count == 4);
- AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID != 0));
+ AddAssert("has 4 rooms", () => container.DrawableRooms.Count == 4);
+ AddAssert("first room removed", () => container.DrawableRooms.All(r => r.Room.RoomID != 0));
- AddStep("select first room", () => container.Rooms.First().TriggerClick());
+ AddStep("select first room", () => container.DrawableRooms.First().TriggerClick());
AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)!));
@@ -137,15 +137,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add rooms", () => RoomManager.AddRooms(4));
- AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
+ AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" });
- AddUntilStep("1 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 1);
+ AddUntilStep("1 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 1);
AddStep("remove filter", () => container.Filter.Value = null);
- AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
+ AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
}
[Test]
@@ -156,13 +156,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
// Todo: What even is this case...?
AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria());
- AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
+ AddUntilStep("5 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 5);
AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
- AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
+ AddUntilStep("2 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
- AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3);
+ AddUntilStep("3 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 3);
}
[Test]
@@ -176,15 +176,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("apply default filter", () => container.Filter.SetDefault());
- AddUntilStep("both rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
+ AddUntilStep("both rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public });
- AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword));
+ AddUntilStep("private room hidden", () => container.DrawableRooms.All(r => !r.Room.HasPassword));
AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private });
- AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword));
+ AddUntilStep("public room hidden", () => container.DrawableRooms.All(r => r.Room.HasPassword));
}
[Test]
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index 53c7873de5..9d65be2a19 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Playlists
public void TestManyRooms()
{
AddStep("add rooms", () => RoomManager.AddRooms(500));
- AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 500);
+ AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 500);
}
[Test]
@@ -45,45 +45,45 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
AddStep("add rooms", () => RoomManager.AddRooms(30));
- AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
+ AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 30);
- AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
+ AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[0]));
- AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.Rooms[2]));
+ AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.DrawableRooms[2]));
AddStep("hold down", () => InputManager.PressButton(MouseButton.Left));
- AddStep("drag to top", () => InputManager.MoveMouseTo(roomsContainer.Rooms[0]));
+ AddStep("drag to top", () => InputManager.MoveMouseTo(roomsContainer.DrawableRooms[0]));
AddAssert("first and second room masked", ()
- => !checkRoomVisible(roomsContainer.Rooms[0]) &&
- !checkRoomVisible(roomsContainer.Rooms[1]));
+ => !checkRoomVisible(roomsContainer.DrawableRooms[0]) &&
+ !checkRoomVisible(roomsContainer.DrawableRooms[1]));
}
[Test]
public void TestScrollSelectedIntoView()
{
AddStep("add rooms", () => RoomManager.AddRooms(30));
- AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
+ AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 30);
- AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
+ AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[0]));
- AddStep("select last room", () => roomsContainer.Rooms[^1].TriggerClick());
+ AddStep("select last room", () => roomsContainer.DrawableRooms[^1].TriggerClick());
- AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms[0]));
- AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1]));
+ AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.DrawableRooms[0]));
+ AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[^1]));
}
[Test]
public void TestEnteringRoomTakesLeaseOnSelection()
{
AddStep("add rooms", () => RoomManager.AddRooms(1));
- AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 1);
+ AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 1);
AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
- AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick());
+ AddStep("select room", () => roomsContainer.DrawableRooms[0].TriggerClick());
AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
- AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick());
+ AddStep("enter room", () => roomsContainer.DrawableRooms[0].TriggerClick());
AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
index 21452727b8..5cb4c9420a 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
@@ -1,9 +1,9 @@
// 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.Linq;
using System.Threading.Tasks;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
@@ -15,23 +15,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
///
public partial class ListingPollingComponent : RoomPollingComponent
{
- public IBindable InitialRoomsReceived => initialRoomsReceived;
- private readonly Bindable initialRoomsReceived = new Bindable();
-
- public readonly Bindable Filter = new Bindable();
-
- [BackgroundDependencyLoader]
- private void load()
- {
- Filter.BindValueChanged(_ =>
- {
- RoomManager.ClearRooms();
- initialRoomsReceived.Value = false;
-
- if (IsLoaded)
- PollImmediately();
- });
- }
+ public required Action RoomsReceived { get; init; }
+ public readonly IBindable Filter = new Bindable();
private GetRoomsRequest? lastPollRequest;
@@ -43,26 +28,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
if (Filter.Value == null)
return base.Poll();
- var tcs = new TaskCompletionSource();
-
lastPollRequest?.Cancel();
+ var tcs = new TaskCompletionSource();
var req = new GetRoomsRequest(Filter.Value);
req.Success += result =>
{
- result = result.Where(r => r.Category != RoomCategory.DailyChallenge).ToList();
-
- foreach (var existing in RoomManager.Rooms.ToArray())
- {
- if (result.All(r => r.RoomID != existing.RoomID))
- RoomManager.RemoveRoom(existing);
- }
-
- foreach (var incoming in result)
- RoomManager.AddOrUpdateRoom(incoming);
-
- initialRoomsReceived.Value = true;
+ RoomsReceived(result.Where(r => r.Category != RoomCategory.DailyChallenge).ToArray());
tcs.SetResult(true);
};
@@ -71,6 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
API.Queue(req);
lastPollRequest = req;
+
return tcs.Task;
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
index 7cee8b3546..f04fd6a096 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
@@ -28,15 +28,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
if (room.RoomID == null)
return base.Poll();
- var tcs = new TaskCompletionSource();
-
lastPollRequest?.Cancel();
+ var tcs = new TaskCompletionSource();
var req = new GetRoomRequest(room.RoomID.Value);
req.Success += result =>
{
- RoomManager.AddOrUpdateRoom(result);
+ room.CopyFrom(result);
tcs.SetResult(true);
};
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index 6eda993f94..6681cbe720 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -7,10 +7,8 @@ using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
@@ -24,17 +22,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class RoomsContainer : CompositeDrawable, IKeyBindingHandler
{
+ public readonly BindableList Rooms = new BindableList();
public readonly Bindable SelectedRoom = new Bindable();
public readonly Bindable Filter = new Bindable();
- public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray();
+ public IReadOnlyList DrawableRooms => roomFlow.FlowingChildren.Cast().ToArray();
- private readonly IBindableList rooms = new BindableList();
private readonly FillFlowContainer roomFlow;
- [Resolved]
- private IRoomManager roomManager { get; set; } = null!;
-
// handle deselection
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
@@ -62,11 +57,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
protected override void LoadComplete()
{
- rooms.CollectionChanged += roomsChanged;
- roomManager.RoomsUpdated += updateSorting;
-
- rooms.BindTo(roomManager.Rooms);
-
+ Rooms.BindCollectionChanged(roomsChanged, true);
Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
}
@@ -155,7 +146,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void addRooms(IEnumerable rooms)
{
foreach (var room in rooms)
- roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = SelectedRoom });
+ {
+ var drawableRoom = new DrawableLoungeRoom(room) { SelectedRoom = SelectedRoom };
+
+ roomFlow.Add(drawableRoom);
+
+ // Always show spotlight playlists at the top of the listing.
+ roomFlow.SetLayoutPosition(drawableRoom, room.Category > RoomCategory.Normal ? float.MinValue : -(room.RoomID ?? 0));
+ }
applyFilterCriteria(Filter.Value);
}
@@ -181,17 +179,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
SelectedRoom.Value = null;
}
- private void updateSorting()
- {
- foreach (var room in roomFlow)
- {
- roomFlow.SetLayoutPosition(room, room.Room.Category > RoomCategory.Normal
- // Always show spotlight playlists at the top of the listing.
- ? float.MinValue
- : -(room.Room.RoomID ?? 0));
- }
- }
-
protected override bool OnClick(ClickEvent e)
{
if (!SelectedRoom.Disabled)
@@ -226,7 +213,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
if (SelectedRoom.Disabled)
return;
- var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
+ var visibleRooms = DrawableRooms.AsEnumerable().Where(r => r.IsPresent);
Room? room;
@@ -246,13 +233,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
#endregion
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (roomManager.IsNotNull())
- roomManager.RoomsUpdated -= updateSorting;
- }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 0e08e398a4..78501a56d7 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -53,8 +53,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AutoSizeAxes = Axes.Both
};
- protected ListingPollingComponent ListingPollingComponent { get; private set; } = null!;
-
protected readonly Bindable SelectedRoom = new Bindable();
[Resolved]
@@ -75,12 +73,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved]
protected OsuConfigManager Config { get; private set; } = null!;
- private IDisposable? joiningRoomOperation { get; set; }
+ private IDisposable? joiningRoomOperation;
private LeasedBindable? selectionLease;
+ private readonly BindableList rooms = new BindableList();
private readonly Bindable filter = new Bindable();
+ private readonly Bindable hasListingResults = new Bindable();
private readonly IBindable operationInProgress = new Bindable();
private readonly IBindable isIdle = new BindableBool();
+ private ListingPollingComponent listingPollingComponent = null!;
private PopoverContainer popoverContainer = null!;
private LoadingLayer loadingLayer = null!;
private RoomsContainer roomsContainer = null!;
@@ -100,7 +101,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
InternalChildren = new Drawable[]
{
- ListingPollingComponent = CreatePollingComponent().With(c => c.Filter.BindTarget = filter),
+ listingPollingComponent = new ListingPollingComponent
+ {
+ RoomsReceived = onListingReceived,
+ Filter = { BindTarget = filter }
+ },
popoverContainer = new PopoverContainer
{
Name = @"Rooms area",
@@ -116,8 +121,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
ScrollbarOverlapsContent = false,
Child = roomsContainer = new RoomsContainer
{
+ Rooms = { BindTarget = rooms },
+ SelectedRoom = { BindTarget = SelectedRoom },
Filter = { BindTarget = filter },
- SelectedRoom = { BindTarget = SelectedRoom }
}
},
},
@@ -178,7 +184,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
// scroll selected room into view on selection.
SelectedRoom.BindValueChanged(val =>
{
- var drawable = roomsContainer.Rooms.FirstOrDefault(r => r.Room == val.NewValue);
+ var drawable = roomsContainer.DrawableRooms.FirstOrDefault(r => r.Room == val.NewValue);
if (drawable != null)
scrollContainer.ScrollIntoView(drawable);
});
@@ -190,7 +196,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
-
isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true);
if (ongoingOperationTracker != null)
@@ -199,11 +204,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
operationInProgress.BindValueChanged(_ => updateLoadingLayer());
}
- ListingPollingComponent.InitialRoomsReceived.BindValueChanged(_ => updateLoadingLayer(), true);
+ hasListingResults.BindValueChanged(_ => updateLoadingLayer());
+
+ filter.BindValueChanged(_ =>
+ {
+ rooms.Clear();
+ hasListingResults.Value = false;
+ listingPollingComponent.PollImmediately();
+ });
updateFilter();
}
+ private void onListingReceived(Room[] result)
+ {
+ Dictionary localRoomsById = rooms.ToDictionary(r => r.RoomID!.Value);
+ Dictionary resultRoomsById = result.ToDictionary(r => r.RoomID!.Value);
+
+ // Remove all local rooms no longer in the result set.
+ rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
+
+ // Add or update local rooms with the result set.
+ foreach (var r in result)
+ {
+ if (localRoomsById.TryGetValue(r.RoomID!.Value, out Room? existingRoom))
+ existingRoom.CopyFrom(r);
+ else
+ rooms.Add(r);
+ }
+
+ hasListingResults.Value = true;
+ }
+
#region Filtering
public void UpdateFilter() => Scheduler.AddOnce(updateFilter);
@@ -267,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
onReturning();
// Poll for any newly-created rooms (including potentially the user's own).
- ListingPollingComponent.PollImmediately();
+ listingPollingComponent.PollImmediately();
}
public override bool OnExiting(ScreenExitEvent e)
@@ -392,11 +424,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
this.Push(CreateRoomSubScreen(room));
}
- public void RefreshRooms() => ListingPollingComponent.PollImmediately();
+ public void RefreshRooms() => listingPollingComponent.PollImmediately();
private void updateLoadingLayer()
{
- if (operationInProgress.Value || !ListingPollingComponent.InitialRoomsReceived.Value)
+ if (operationInProgress.Value || !hasListingResults.Value)
loadingLayer.Show();
else
loadingLayer.Hide();
@@ -405,11 +437,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void updatePollingRate(bool isCurrentScreen)
{
if (!isCurrentScreen)
- ListingPollingComponent.TimeBetweenPolls.Value = 0;
+ listingPollingComponent.TimeBetweenPolls.Value = 0;
else
- ListingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
+ listingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
- Logger.Log($"Polling adjusted (listing: {ListingPollingComponent.TimeBetweenPolls.Value})");
+ Logger.Log($"Polling adjusted (listing: {listingPollingComponent.TimeBetweenPolls.Value})");
}
protected abstract OsuButton CreateNewRoomButton();
@@ -421,7 +453,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected abstract Room CreateNewRoom();
protected abstract RoomSubScreen CreateRoomSubScreen(Room room);
-
- protected abstract ListingPollingComponent CreatePollingComponent();
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index 873a9cde88..3cf873ec78 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -79,8 +79,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
- protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
-
protected override void JoinInternal(Room room, string? password, Action onSuccess, Action onFailure)
{
client.JoinRoom(room, password).ContinueWith(result =>
@@ -109,37 +107,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
base.OpenNewRoom(room);
}
-
- private partial class MultiplayerListingPollingComponent : ListingPollingComponent
- {
- [Resolved]
- private MultiplayerClient client { get; set; } = null!;
-
- private readonly IBindable isConnected = new Bindable();
-
- [BackgroundDependencyLoader]
- private void load()
- {
- isConnected.BindTo(client.IsConnected);
- isConnected.BindValueChanged(_ => Scheduler.AddOnce(poll), true);
- }
-
- private void poll()
- {
- if (isConnected.Value && IsLoaded)
- PollImmediately();
- }
-
- protected override Task Poll()
- {
- if (!isConnected.Value)
- return Task.CompletedTask;
-
- if (client.Room != null)
- return Task.CompletedTask;
-
- return base.Poll();
- }
- }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index 6ed367328c..26eae50797 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -11,7 +11,6 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
@@ -87,8 +86,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
- protected override ListingPollingComponent CreatePollingComponent() => new ListingPollingComponent();
-
private enum PlaylistsCategory
{
Any,
From f146a7d116bbebec4880c8a3dd7124d20dc58022 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 21:09:58 +0900
Subject: [PATCH 022/349] Remove `RoomManager` and related components
---
.../TestSceneLoungeRoomsContainer.cs | 54 ++++++------
.../TestSceneMultiplayerLoungeSubScreen.cs | 33 +++++---
.../TestScenePlaylistsLoungeSubScreen.cs | 30 +++----
.../TestScenePlaylistsMatchSettingsOverlay.cs | 2 -
.../Components/ListingPollingComponent.cs | 14 +++-
.../OnlinePlay/Components/RoomManager.cs | 82 -------------------
.../Components/RoomPollingComponent.cs | 18 ----
.../Components/SelectionPollingComponent.cs | 14 +++-
osu.Game/Screens/OnlinePlay/IRoomManager.cs | 42 ----------
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 12 +--
.../Screens/OnlinePlay/OnlinePlayScreen.cs | 5 --
.../IOnlinePlayTestSceneDependencies.cs | 5 --
.../Visual/OnlinePlay/OnlinePlayTestScene.cs | 32 +++++++-
.../OnlinePlayTestSceneDependencies.cs | 3 -
.../Visual/OnlinePlay/TestRoomManager.cs | 59 -------------
.../OnlinePlay/TestRoomRequestsHandler.cs | 3 +-
16 files changed, 121 insertions(+), 287 deletions(-)
delete mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
delete mode 100644 osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
delete mode 100644 osu.Game/Screens/OnlinePlay/IRoomManager.cs
delete mode 100644 osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 10df77f88c..9daad960c7 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -3,6 +3,7 @@
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
@@ -19,8 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
{
- protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
-
+ private BindableList rooms = null!;
private RoomsContainer container = null!;
public override void SetUpSteps()
@@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create container", () =>
{
+ rooms = new BindableList();
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.X,
@@ -36,9 +37,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 0.5f,
-
Child = container = new RoomsContainer
{
+ Rooms = { BindTarget = rooms },
SelectedRoom = { BindTarget = SelectedRoom }
}
};
@@ -48,7 +49,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestBasicListChanges()
{
- AddStep("add rooms", () => RoomManager.AddRooms(5, withSpotlightRooms: true));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(5, withSpotlightRooms: true)));
AddAssert("has 5 rooms", () => container.DrawableRooms.Count == 5);
@@ -56,49 +57,50 @@ namespace osu.Game.Tests.Visual.Multiplayer
.SkipWhile(r => r.Room.Category == RoomCategory.Spotlight)
.All(r => r.Room.Category == RoomCategory.Normal));
- AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0)));
+ AddStep("remove first room", () => rooms.RemoveAt(0));
AddAssert("has 4 rooms", () => container.DrawableRooms.Count == 4);
AddAssert("first room removed", () => container.DrawableRooms.All(r => r.Room.RoomID != 0));
AddStep("select first room", () => container.DrawableRooms.First().TriggerClick());
- AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
+ AddAssert("first spotlight selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight)));
- AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)!));
- AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
+ AddStep("remove last room", () => rooms.RemoveAt(rooms.Count - 1));
+ AddAssert("first spotlight still selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight)));
- AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight)));
+ AddStep("remove spotlight room", () => rooms.RemoveAll(r => r.Category == RoomCategory.Spotlight));
AddAssert("selection vacated", () => checkRoomSelected(null));
}
[Test]
public void TestKeyboardNavigation()
{
- AddStep("add rooms", () => RoomManager.AddRooms(3));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3)));
AddAssert("no selection", () => checkRoomSelected(null));
press(Key.Down);
- AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
+ AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
press(Key.Up);
- AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
+ AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
press(Key.Down);
press(Key.Down);
- AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last()));
+ AddAssert("last room selected", () => checkRoomSelected(container.DrawableRooms.Last().Room));
}
[Test]
public void TestKeyboardNavigationAfterOrderChange()
{
- AddStep("add rooms", () => RoomManager.AddRooms(3));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3)));
AddStep("reorder rooms", () =>
{
- var room = RoomManager.Rooms[1];
+ var room = rooms[1];
+ rooms.Remove(room);
- RoomManager.RemoveRoom(room);
- RoomManager.AddOrUpdateRoom(room);
+ room.RoomID += 3;
+ rooms.Add(room);
});
AddAssert("no selection", () => checkRoomSelected(null));
@@ -116,12 +118,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestClickDeselection()
{
- AddStep("add room", () => RoomManager.AddRooms(1));
+ AddStep("add room", () => rooms.AddRange(GenerateRooms(1)));
AddAssert("no selection", () => checkRoomSelected(null));
press(Key.Down);
- AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
+ AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
AddStep("click away", () => InputManager.Click(MouseButton.Left));
AddAssert("no selection", () => checkRoomSelected(null));
@@ -135,11 +137,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestStringFiltering()
{
- AddStep("add rooms", () => RoomManager.AddRooms(4));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(4)));
AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
- AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" });
+ AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = rooms.First().Name });
AddUntilStep("1 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 1);
@@ -151,8 +153,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestRulesetFiltering()
{
- AddStep("add rooms", () => RoomManager.AddRooms(2, new OsuRuleset().RulesetInfo));
- AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(2, new OsuRuleset().RulesetInfo)));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, new CatchRuleset().RulesetInfo)));
// Todo: What even is this case...?
AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria());
@@ -170,8 +172,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add rooms", () =>
{
- RoomManager.AddRooms(1, withPassword: true);
- RoomManager.AddRooms(1, withPassword: false);
+ rooms.AddRange(GenerateRooms(1, withPassword: true));
+ rooms.AddRange(GenerateRooms(1, withPassword: false));
});
AddStep("apply default filter", () => container.Filter.SetDefault());
@@ -190,7 +192,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPasswordProtectedRooms()
{
- AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true));
+ AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, withPassword: true)));
}
private bool checkRoomSelected(Room? room) => SelectedRoom.Value == room;
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index eb649acd2d..b4ec9d5858 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -8,8 +8,8 @@ using osu.Framework.Graphics.UserInterface;
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.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
@@ -18,11 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerLoungeSubScreen : MultiplayerTestScene
{
- protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
-
- private LoungeSubScreen loungeScreen = null!;
-
- private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType().First();
+ private MultiplayerLoungeSubScreen loungeScreen = null!;
public TestSceneMultiplayerLoungeSubScreen()
: base(false)
@@ -40,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestJoinRoomWithoutPassword()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false));
+ createRooms(GenerateRooms(1, withPassword: false));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
@@ -50,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPopoverHidesOnBackButton()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
@@ -70,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPopoverHidesOnLeavingScreen()
{
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
@@ -86,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(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 +101,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(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 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(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 +135,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
- AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
+ createRooms(GenerateRooms(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,6 +145,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("room joined", () => MultiplayerClient.RoomJoined);
}
+ private void createRooms(params Room[] rooms)
+ {
+ AddStep("create rooms", () =>
+ {
+ foreach (var room in rooms)
+ API.Queue(new CreateRoomRequest(room));
+ });
+
+ AddStep("refresh lounge", () => loungeScreen.RefreshRooms());
+ }
+
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 9d65be2a19..94a81ecdc7 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -17,8 +17,6 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
{
- protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
-
private TestLoungeSubScreen loungeScreen = null!;
public override void SetUpSteps()
@@ -26,7 +24,6 @@ namespace osu.Game.Tests.Visual.Playlists
base.SetUpSteps();
AddStep("push screen", () => LoadScreen(loungeScreen = new TestLoungeSubScreen()));
-
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
}
@@ -35,8 +32,7 @@ namespace osu.Game.Tests.Visual.Playlists
[Test]
public void TestManyRooms()
{
- AddStep("add rooms", () => RoomManager.AddRooms(500));
- AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 500);
+ createRooms(GenerateRooms(500));
}
[Test]
@@ -44,10 +40,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
- AddStep("add rooms", () => RoomManager.AddRooms(30));
- AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 30);
-
- AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[0]));
+ createRooms(GenerateRooms(30));
AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.DrawableRooms[2]));
AddStep("hold down", () => InputManager.PressButton(MouseButton.Left));
@@ -61,10 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists
[Test]
public void TestScrollSelectedIntoView()
{
- AddStep("add rooms", () => RoomManager.AddRooms(30));
- AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 30);
-
- AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[0]));
+ createRooms(GenerateRooms(30));
AddStep("select last room", () => roomsContainer.DrawableRooms[^1].TriggerClick());
@@ -75,8 +65,7 @@ namespace osu.Game.Tests.Visual.Playlists
[Test]
public void TestEnteringRoomTakesLeaseOnSelection()
{
- AddStep("add rooms", () => RoomManager.AddRooms(1));
- AddUntilStep("wait for rooms", () => roomsContainer.DrawableRooms.Count == 1);
+ createRooms(GenerateRooms(1));
AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
@@ -95,6 +84,17 @@ namespace osu.Game.Tests.Visual.Playlists
loungeScreen.ChildrenOfType().First().ScreenSpaceDrawQuad
.Contains(room.ScreenSpaceDrawQuad.Centre);
+ private void createRooms(params Room[] rooms)
+ {
+ AddStep("create rooms", () =>
+ {
+ foreach (var room in rooms)
+ API.Queue(new CreateRoomRequest(room));
+ });
+
+ AddStep("refresh lounge", () => loungeScreen.RefreshRooms());
+ }
+
private partial class TestLoungeSubScreen : PlaylistsLoungeSubScreen
{
public new Bindable SelectedRoom => base.SelectedRoom;
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 51e39e1b7f..f7b0bc0d58 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -17,8 +17,6 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsMatchSettingsOverlay : OnlinePlayTestScene
{
- protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
-
private TestRoomSettings settings = null!;
private Func? handleRequest;
diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
index 5cb4c9420a..1495f97de4 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
@@ -4,17 +4,23 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Game.Online;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Screens.OnlinePlay.Components
{
///
- /// A that polls for the lounge listing.
+ /// A that polls for the lounge listing.
///
- public partial class ListingPollingComponent : RoomPollingComponent
+ public partial class ListingPollingComponent : PollingComponent
{
+ [Resolved]
+ private IAPIProvider api { get; set; } = null!;
+
public required Action RoomsReceived { get; init; }
public readonly IBindable Filter = new Bindable();
@@ -22,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
protected override Task Poll()
{
- if (!API.IsLoggedIn)
+ if (!api.IsLoggedIn)
return base.Poll();
if (Filter.Value == null)
@@ -41,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
req.Failure += _ => tcs.SetResult(false);
- API.Queue(req);
+ api.Queue(req);
lastPollRequest = req;
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
deleted file mode 100644
index a1b61ea7a3..0000000000
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ /dev/null
@@ -1,82 +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.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using osu.Framework.Bindables;
-using osu.Framework.Development;
-using osu.Framework.Graphics;
-using osu.Framework.Logging;
-using osu.Game.Online.Rooms;
-
-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;
-
- private readonly BindableList rooms = new BindableList();
-
- public IBindableList Rooms => rooms;
-
- public RoomManager()
- {
- RelativeSizeAxes = Axes.Both;
- }
-
- private readonly HashSet ignoredRooms = new HashSet();
-
- public void AddOrUpdateRoom(Room room)
- {
- Debug.Assert(ThreadSafety.IsUpdateThread);
- Debug.Assert(room.RoomID != null);
-
- if (ignoredRooms.Contains(room.RoomID.Value))
- return;
-
- try
- {
- var existing = rooms.FirstOrDefault(e => e.RoomID == room.RoomID);
- if (existing == null)
- rooms.Add(room);
- else
- existing.CopyFrom(room);
- }
- catch (Exception ex)
- {
- Logger.Error(ex, $"Failed to update room: {room.Name}.");
-
- ignoredRooms.Add(room.RoomID.Value);
- rooms.Remove(room);
- }
-
- notifyRoomsUpdated();
- }
-
- public void RemoveRoom(Room room)
- {
- Debug.Assert(ThreadSafety.IsUpdateThread);
-
- rooms.Remove(room);
- notifyRoomsUpdated();
- }
-
- public void ClearRooms()
- {
- Debug.Assert(ThreadSafety.IsUpdateThread);
-
- rooms.Clear();
- notifyRoomsUpdated();
- }
-
- private void notifyRoomsUpdated()
- {
- Scheduler.AddOnce(invokeRoomsUpdated);
-
- void invokeRoomsUpdated() => RoomsUpdated?.Invoke();
- }
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
deleted file mode 100644
index 0ba7f20f1c..0000000000
--- a/osu.Game/Screens/OnlinePlay/Components/RoomPollingComponent.cs
+++ /dev/null
@@ -1,18 +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 osu.Framework.Allocation;
-using osu.Game.Online;
-using osu.Game.Online.API;
-
-namespace osu.Game.Screens.OnlinePlay.Components
-{
- public abstract partial class RoomPollingComponent : PollingComponent
- {
- [Resolved]
- protected IAPIProvider API { get; private set; } = null!;
-
- [Resolved]
- protected IRoomManager RoomManager { get; private set; } = null!;
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
index f04fd6a096..bfa059f72e 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
@@ -2,15 +2,21 @@
// See the LICENCE file in the repository root for full licence text.
using System.Threading.Tasks;
+using osu.Framework.Allocation;
+using osu.Game.Online;
+using osu.Game.Online.API;
using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
///
- /// A that polls for the currently-selected room.
+ /// A that polls for and updates a room.
///
- public partial class SelectionPollingComponent : RoomPollingComponent
+ public partial class SelectionPollingComponent : PollingComponent
{
+ [Resolved]
+ private IAPIProvider api { get; set; } = null!;
+
private readonly Room room;
public SelectionPollingComponent(Room room)
@@ -22,7 +28,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
protected override Task Poll()
{
- if (!API.IsLoggedIn)
+ if (!api.IsLoggedIn)
return base.Poll();
if (room.RoomID == null)
@@ -41,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
req.Failure += _ => tcs.SetResult(false);
- API.Queue(req);
+ api.Queue(req);
lastPollRequest = req;
diff --git a/osu.Game/Screens/OnlinePlay/IRoomManager.cs b/osu.Game/Screens/OnlinePlay/IRoomManager.cs
deleted file mode 100644
index 8ecb1dd7e0..0000000000
--- a/osu.Game/Screens/OnlinePlay/IRoomManager.cs
+++ /dev/null
@@ -1,42 +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 osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Game.Online.Rooms;
-
-namespace osu.Game.Screens.OnlinePlay
-{
- [Cached(typeof(IRoomManager))]
- public interface IRoomManager
- {
- ///
- /// Invoked when the s have been updated.
- ///
- event Action RoomsUpdated;
-
- ///
- /// All the active s.
- ///
- IBindableList Rooms { get; }
-
- ///
- /// Adds a to this .
- /// If already existing, the local room will be updated with the given one.
- ///
- /// The incoming .
- void AddOrUpdateRoom(Room room);
-
- ///
- /// Removes a from this .
- ///
- /// The to remove.
- void RemoveRoom(Room room);
-
- ///
- /// Removes all s from this .
- ///
- void ClearRooms();
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 78501a56d7..6c383f1bf6 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -54,6 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
};
protected readonly Bindable SelectedRoom = new Bindable();
+ protected readonly BindableList Rooms = new BindableList();
[Resolved]
private MusicController music { get; set; } = null!;
@@ -76,7 +77,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private IDisposable? joiningRoomOperation;
private LeasedBindable? selectionLease;
- private readonly BindableList rooms = new BindableList();
private readonly Bindable filter = new Bindable();
private readonly Bindable hasListingResults = new Bindable();
private readonly IBindable operationInProgress = new Bindable();
@@ -121,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
ScrollbarOverlapsContent = false,
Child = roomsContainer = new RoomsContainer
{
- Rooms = { BindTarget = rooms },
+ Rooms = { BindTarget = Rooms },
SelectedRoom = { BindTarget = SelectedRoom },
Filter = { BindTarget = filter },
}
@@ -208,7 +208,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
filter.BindValueChanged(_ =>
{
- rooms.Clear();
+ Rooms.Clear();
hasListingResults.Value = false;
listingPollingComponent.PollImmediately();
});
@@ -218,11 +218,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void onListingReceived(Room[] result)
{
- Dictionary localRoomsById = rooms.ToDictionary(r => r.RoomID!.Value);
+ Dictionary localRoomsById = Rooms.ToDictionary(r => r.RoomID!.Value);
Dictionary resultRoomsById = result.ToDictionary(r => r.RoomID!.Value);
// Remove all local rooms no longer in the result set.
- rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
+ Rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
// Add or update local rooms with the result set.
foreach (var r in result)
@@ -230,7 +230,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
if (localRoomsById.TryGetValue(r.RoomID!.Value, out Room? existingRoom))
existingRoom.CopyFrom(r);
else
- rooms.Add(r);
+ Rooms.Add(r);
}
hasListingResults.Value = true;
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
index 8988c82dee..812e42479b 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs
@@ -11,7 +11,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Screens.Menu;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Users;
@@ -39,9 +38,6 @@ namespace osu.Game.Screens.OnlinePlay
[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!;
@@ -65,7 +61,6 @@ namespace osu.Game.Screens.OnlinePlay
{
screenStack,
new Header(ScreenTitle, screenStack),
- roomManager,
ongoingOperationTracker,
}
};
diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
index 8ddc5325db..5780cf6eff 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
@@ -18,11 +18,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
Bindable SelectedRoom { get; }
- ///
- /// The cached
- ///
- IRoomManager RoomManager { get; }
-
///
/// The cached .
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
index 3f6c175fbd..c3a5e1c3ec 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
@@ -10,7 +10,9 @@ using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
+using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay;
namespace osu.Game.Tests.Visual.OnlinePlay
@@ -21,7 +23,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlayTestSceneDependencies
{
public Bindable SelectedRoom => OnlinePlayDependencies.SelectedRoom;
- public IRoomManager RoomManager => OnlinePlayDependencies.RoomManager;
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies.OngoingOperationTracker;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies.AvailabilityTracker;
public TestUserLookupCache UserLookupCache => OnlinePlayDependencies.UserLookupCache;
@@ -34,9 +35,13 @@ namespace osu.Game.Tests.Visual.OnlinePlay
protected override Container Content => content;
+ [Resolved]
+ private RulesetStore rulesets { get; set; } = null!;
+
private readonly Container content;
private readonly Container drawableDependenciesContainer;
private DelegatedDependencyContainer dependencies = null!;
+ private int currentRoomId;
protected OnlinePlayTestScene()
{
@@ -93,6 +98,31 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
protected virtual OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new OnlinePlayTestSceneDependencies();
+ protected Room[] GenerateRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withSpotlightRooms = false)
+ {
+ Room[] rooms = new Room[count];
+
+ // Can't reference Osu ruleset project here.
+ ruleset ??= rulesets.GetRuleset(0)!;
+
+ for (int i = 0; i < count; i++)
+ {
+ rooms[i] = new Room
+ {
+ RoomID = currentRoomId++,
+ Name = $@"Room {currentRoomId}",
+ Host = new APIUser { Username = @"Host" },
+ Duration = TimeSpan.FromSeconds(10),
+ Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal,
+ Password = withPassword ? @"password" : null,
+ PlaylistItemStats = new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] },
+ Playlist = [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }]
+ };
+ }
+
+ return rooms;
+ }
+
///
/// A providing a mutable lookup source for online play dependencies.
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
index 203922c057..cc448beea0 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -19,7 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public class OnlinePlayTestSceneDependencies : IReadOnlyDependencyContainer, IOnlinePlayTestSceneDependencies
{
public Bindable SelectedRoom { get; }
- public IRoomManager RoomManager { get; }
public OngoingOperationTracker OngoingOperationTracker { get; }
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
public TestRoomRequestsHandler RequestsHandler { get; }
@@ -40,7 +39,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
RequestsHandler = new TestRoomRequestsHandler();
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
- RoomManager = new TestRoomManager();
UserLookupCache = new TestUserLookupCache();
BeatmapLookupCache = new BeatmapLookupCache();
@@ -48,7 +46,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
CacheAs(RequestsHandler);
CacheAs(SelectedRoom);
- CacheAs(RoomManager);
CacheAs(OngoingOperationTracker);
CacheAs(AvailabilityTracker);
CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum));
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
deleted file mode 100644
index bff2753929..0000000000
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ /dev/null
@@ -1,59 +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 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;
-using osu.Game.Screens.OnlinePlay.Components;
-
-namespace osu.Game.Tests.Visual.OnlinePlay
-{
- ///
- /// A very simple for use in online play test scenes.
- ///
- public partial class TestRoomManager : RoomManager
- {
- private int currentRoomId;
-
- [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
- {
- Name = $@"Room {currentRoomId}",
- Host = new APIUser { Username = @"Host" },
- Duration = TimeSpan.FromSeconds(10),
- Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal,
- Password = withPassword ? @"password" : null,
- PlaylistItemStats = new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] },
- Playlist = [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }]
- });
- }
- }
-
- public void AddRoom(Room room)
- {
- room.RoomID = -currentRoomId;
-
- var req = new CreateRoomRequest(room);
- req.Success += AddOrUpdateRoom;
- api.Queue(req);
-
- currentRoomId++;
- }
- }
-}
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
index c9149bda22..63bc9325fa 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
@@ -36,8 +36,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
private int currentScoreId = 1;
///
- /// Handles an API request, while also updating the local state to match
- /// how the server would eventually respond and update an .
+ /// Handles an API request, while also updating the local state to match how the server would eventually respond.
///
/// The API request to handle.
/// The local user to store in responses where required.
From 1b07b6d16f49fd06572c3366685a08f2a2641669 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 21:48:59 +0900
Subject: [PATCH 023/349] Remove selected room leasing, make bindables private
I believe once upon a time the `SelectedRoom` bindable used to be bound
to `RoomManager.JoinedRoom` or similar. But now it's effectively private
to the lounge subscreen and so a lease is unnecessary.
---
.../TestScenePlaylistsLoungeSubScreen.cs | 28 +------------
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 39 ++++++-------------
2 files changed, 13 insertions(+), 54 deletions(-)
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index 94a81ecdc7..35bf6dc28a 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -3,7 +3,6 @@
using System.Linq;
using NUnit.Framework;
-using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
@@ -17,13 +16,13 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
{
- private TestLoungeSubScreen loungeScreen = null!;
+ private PlaylistsLoungeSubScreen loungeScreen = null!;
public override void SetUpSteps()
{
base.SetUpSteps();
- AddStep("push screen", () => LoadScreen(loungeScreen = new TestLoungeSubScreen()));
+ AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen()));
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
}
@@ -62,24 +61,6 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.DrawableRooms[^1]));
}
- [Test]
- public void TestEnteringRoomTakesLeaseOnSelection()
- {
- createRooms(GenerateRooms(1));
-
- AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
-
- AddStep("select room", () => roomsContainer.DrawableRooms[0].TriggerClick());
- AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
-
- AddStep("enter room", () => roomsContainer.DrawableRooms[0].TriggerClick());
-
- AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
-
- AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
- AddAssert("selected room is disabled", () => loungeScreen.SelectedRoom.Disabled);
- }
-
private bool checkRoomVisible(DrawableRoom room) =>
loungeScreen.ChildrenOfType().First().ScreenSpaceDrawQuad
.Contains(room.ScreenSpaceDrawQuad.Centre);
@@ -94,10 +75,5 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("refresh lounge", () => loungeScreen.RefreshRooms());
}
-
- private partial class TestLoungeSubScreen : PlaylistsLoungeSubScreen
- {
- public new Bindable SelectedRoom => base.SelectedRoom;
- }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 6c383f1bf6..7bb0c67990 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen
{
- SelectedRoom = { BindTarget = SelectedRoom }
+ SelectedRoom = { BindTarget = selectedRoom }
};
protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby();
@@ -53,9 +53,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AutoSizeAxes = Axes.Both
};
- protected readonly Bindable SelectedRoom = new Bindable();
- protected readonly BindableList Rooms = new BindableList();
-
[Resolved]
private MusicController music { get; set; } = null!;
@@ -75,8 +72,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected OsuConfigManager Config { get; private set; } = null!;
private IDisposable? joiningRoomOperation;
- private LeasedBindable? selectionLease;
+ private readonly Bindable selectedRoom = new Bindable();
+ private readonly BindableList rooms = new BindableList();
private readonly Bindable filter = new Bindable();
private readonly Bindable hasListingResults = new Bindable();
private readonly IBindable operationInProgress = new Bindable();
@@ -121,8 +119,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
ScrollbarOverlapsContent = false,
Child = roomsContainer = new RoomsContainer
{
- Rooms = { BindTarget = Rooms },
- SelectedRoom = { BindTarget = SelectedRoom },
+ Rooms = { BindTarget = rooms },
+ SelectedRoom = { BindTarget = selectedRoom },
Filter = { BindTarget = filter },
}
},
@@ -182,7 +180,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
};
// scroll selected room into view on selection.
- SelectedRoom.BindValueChanged(val =>
+ selectedRoom.BindValueChanged(val =>
{
var drawable = roomsContainer.DrawableRooms.FirstOrDefault(r => r.Room == val.NewValue);
if (drawable != null)
@@ -208,7 +206,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
filter.BindValueChanged(_ =>
{
- Rooms.Clear();
+ rooms.Clear();
hasListingResults.Value = false;
listingPollingComponent.PollImmediately();
});
@@ -218,11 +216,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void onListingReceived(Room[] result)
{
- Dictionary localRoomsById = Rooms.ToDictionary(r => r.RoomID!.Value);
+ Dictionary localRoomsById = rooms.ToDictionary(r => r.RoomID!.Value);
Dictionary resultRoomsById = result.ToDictionary(r => r.RoomID!.Value);
// Remove all local rooms no longer in the result set.
- Rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
+ rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
// Add or update local rooms with the result set.
foreach (var r in result)
@@ -230,7 +228,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
if (localRoomsById.TryGetValue(r.RoomID!.Value, out Room? existingRoom))
existingRoom.CopyFrom(r);
else
- Rooms.Add(r);
+ rooms.Add(r);
}
hasListingResults.Value = true;
@@ -286,14 +284,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
base.OnResuming(e);
- Debug.Assert(selectionLease != null);
-
- selectionLease.Return();
- selectionLease = null;
-
- if (SelectedRoom.Value?.RoomID == null)
- SelectedRoom.Value = new Room();
-
music.EnsurePlayingSomething();
onReturning();
@@ -415,14 +405,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
OpenNewRoom(room ?? CreateNewRoom());
});
- protected virtual void OpenNewRoom(Room room)
- {
- selectionLease = SelectedRoom.BeginLease(false);
- Debug.Assert(selectionLease != null);
- selectionLease.Value = room;
-
- this.Push(CreateRoomSubScreen(room));
- }
+ protected virtual void OpenNewRoom(Room room) => this.Push(CreateRoomSubScreen(room));
public void RefreshRooms() => listingPollingComponent.PollImmediately();
From 74ccac37ae665ea2a9a603316077453520a8b9de Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 21:57:18 +0900
Subject: [PATCH 024/349] Encapsulate RoomsContainer scroll a bit better
---
.../TestSceneLoungeRoomsContainer.cs | 4 +--
.../Lounge/Components/RoomsContainer.cs | 35 ++++++++++++-------
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 26 +++-----------
3 files changed, 30 insertions(+), 35 deletions(-)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 9daad960c7..772eb91174 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -32,13 +32,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
rooms = new BindableList();
Child = new PopoverContainer
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 0.5f,
Child = container = new RoomsContainer
{
+ RelativeSizeAxes = Axes.Both,
Rooms = { BindTarget = rooms },
SelectedRoom = { BindTarget = SelectedRoom }
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index 6681cbe720..65f969bc7b 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -13,6 +13,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms;
@@ -28,6 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
public IReadOnlyList DrawableRooms => roomFlow.FlowingChildren.Cast().ToArray();
+ private readonly ScrollContainer scroll;
private readonly FillFlowContainer roomFlow;
// handle deselection
@@ -35,28 +37,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
public RoomsContainer()
{
- RelativeSizeAxes = Axes.X;
- AutoSizeAxes = Axes.Y;
-
- // account for the fact we are in a scroll container and want a bit of spacing from the scroll bar.
- Padding = new MarginPadding { Right = 5 };
-
- InternalChild = new OsuContextMenuContainer
+ InternalChild = scroll = new OsuScrollContainer
{
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Child = roomFlow = new FillFlowContainer
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarOverlapsContent = false,
+ Padding = new MarginPadding { Right = 5 },
+ Child = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(10),
+ Child = roomFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(10),
+ }
}
};
}
protected override void LoadComplete()
{
+ SelectedRoom.BindValueChanged(onSelectedRoomChanged, true);
Rooms.BindCollectionChanged(roomsChanged, true);
Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
}
@@ -119,6 +122,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
}
+ private void onSelectedRoomChanged(ValueChangedEvent room)
+ {
+ // scroll selected room into view on selection.
+ var drawable = DrawableRooms.FirstOrDefault(r => r.Room == room.NewValue);
+ if (drawable != null)
+ scroll.ScrollIntoView(drawable);
+ }
+
private void roomsChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 7bb0c67990..1877244c03 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -17,7 +17,6 @@ using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Configuration;
-using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Online.API;
@@ -82,7 +81,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private ListingPollingComponent listingPollingComponent = null!;
private PopoverContainer popoverContainer = null!;
private LoadingLayer loadingLayer = null!;
- private RoomsContainer roomsContainer = null!;
private SearchTextBox searchTextBox = null!;
protected Dropdown StatusDropdown { get; private set; } = null!;
@@ -95,8 +93,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
if (idleTracker != null)
isIdle.BindTo(idleTracker.IsIdle);
- OsuScrollContainer scrollContainer;
-
InternalChildren = new Drawable[]
{
listingPollingComponent = new ListingPollingComponent
@@ -113,17 +109,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
Horizontal = WaveOverlayContainer.WIDTH_PADDING,
Top = Header.HEIGHT + controls_area_height + 20,
},
- Child = scrollContainer = new OsuScrollContainer
+ Child = new RoomsContainer
{
RelativeSizeAxes = Axes.Both,
- ScrollbarOverlapsContent = false,
- Child = roomsContainer = new RoomsContainer
- {
- Rooms = { BindTarget = rooms },
- SelectedRoom = { BindTarget = selectedRoom },
- Filter = { BindTarget = filter },
- }
- },
+ Rooms = { BindTarget = rooms },
+ SelectedRoom = { BindTarget = selectedRoom },
+ Filter = { BindTarget = filter },
+ }
},
loadingLayer = new LoadingLayer(true),
new FillFlowContainer
@@ -178,14 +170,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
},
},
};
-
- // scroll selected room into view on selection.
- selectedRoom.BindValueChanged(val =>
- {
- var drawable = roomsContainer.DrawableRooms.FirstOrDefault(r => r.Room == val.NewValue);
- if (drawable != null)
- scrollContainer.ScrollIntoView(drawable);
- });
}
protected override void LoadComplete()
From 43928c94db5b4695b2baab8acfb41d58198322aa Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 22:03:22 +0900
Subject: [PATCH 025/349] Remove remaining bindables
---
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 17 +++++++----------
.../Multiplayer/MultiplayerLoungeSubScreen.cs | 3 ---
.../OnlinePlay/TestRoomRequestsHandler.cs | 2 --
3 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 1877244c03..2e78e88ccf 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen
{
- SelectedRoom = { BindTarget = selectedRoom }
+ SelectedRoom = { BindTarget = roomsContainer.SelectedRoom }
};
protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby();
@@ -72,12 +72,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private IDisposable? joiningRoomOperation;
- private readonly Bindable selectedRoom = new Bindable();
- private readonly BindableList rooms = new BindableList();
private readonly Bindable filter = new Bindable();
private readonly Bindable hasListingResults = new Bindable();
private readonly IBindable operationInProgress = new Bindable();
private readonly IBindable isIdle = new BindableBool();
+ private RoomsContainer roomsContainer = null!;
private ListingPollingComponent listingPollingComponent = null!;
private PopoverContainer popoverContainer = null!;
private LoadingLayer loadingLayer = null!;
@@ -109,11 +108,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
Horizontal = WaveOverlayContainer.WIDTH_PADDING,
Top = Header.HEIGHT + controls_area_height + 20,
},
- Child = new RoomsContainer
+ Child = roomsContainer = new RoomsContainer
{
RelativeSizeAxes = Axes.Both,
- Rooms = { BindTarget = rooms },
- SelectedRoom = { BindTarget = selectedRoom },
Filter = { BindTarget = filter },
}
},
@@ -190,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
filter.BindValueChanged(_ =>
{
- rooms.Clear();
+ roomsContainer.Rooms.Clear();
hasListingResults.Value = false;
listingPollingComponent.PollImmediately();
});
@@ -200,11 +197,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void onListingReceived(Room[] result)
{
- Dictionary localRoomsById = rooms.ToDictionary(r => r.RoomID!.Value);
+ Dictionary localRoomsById = roomsContainer.Rooms.ToDictionary(r => r.RoomID!.Value);
Dictionary resultRoomsById = result.ToDictionary(r => r.RoomID!.Value);
// Remove all local rooms no longer in the result set.
- rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
+ roomsContainer.Rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
// Add or update local rooms with the result set.
foreach (var r in result)
@@ -212,7 +209,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
if (localRoomsById.TryGetValue(r.RoomID!.Value, out Room? existingRoom))
existingRoom.CopyFrom(r);
else
- rooms.Add(r);
+ roomsContainer.Rooms.Add(r);
}
hasListingResults.Value = true;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index 3cf873ec78..6191cfd975 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -3,9 +3,7 @@
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.Graphics;
@@ -15,7 +13,6 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
index 63bc9325fa..617a4cff79 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs
@@ -15,7 +15,6 @@ using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Beatmaps;
using osu.Game.Utils;
@@ -28,7 +27,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public class TestRoomRequestsHandler
{
public IReadOnlyList ServerSideRooms => serverSideRooms;
-
private readonly List serverSideRooms = new List();
private int currentRoomId = 1;
From 24cc77287e5e715a0fc684999f0a9aadd1355380 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 22:21:04 +0900
Subject: [PATCH 026/349] Refactor polling components (namespace/namings)
---
.../Visual/Multiplayer/TestSceneMultiplayer.cs | 3 +--
.../LoungePollingComponent.cs} | 4 ++--
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 17 ++++++++---------
.../Playlists/PlaylistsRoomSubScreen.cs | 8 ++++----
.../PlaylistsRoomUpdater.cs} | 6 +++---
5 files changed, 18 insertions(+), 20 deletions(-)
rename osu.Game/Screens/OnlinePlay/{Components/ListingPollingComponent.cs => Lounge/LoungePollingComponent.cs} (92%)
rename osu.Game/Screens/OnlinePlay/{Components/SelectionPollingComponent.cs => Playlists/PlaylistsRoomUpdater.cs} (88%)
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index 0966c61a3a..a87216287d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -33,7 +33,6 @@ using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
@@ -806,7 +805,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for room", () => this.ChildrenOfType().Any());
AddStep("select room", () => InputManager.Key(Key.Down));
- AddStep("disable polling", () => this.ChildrenOfType().Single().TimeBetweenPolls.Value = 0);
+ AddStep("disable polling", () => this.ChildrenOfType().Single().TimeBetweenPolls.Value = 0);
AddStep("change server-side settings", () =>
{
multiplayerClient.ServerSideRooms[0].Name = "New name";
diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungePollingComponent.cs
similarity index 92%
rename from osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
rename to osu.Game/Screens/OnlinePlay/Lounge/LoungePollingComponent.cs
index 1495f97de4..420a96cf8a 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungePollingComponent.cs
@@ -11,12 +11,12 @@ using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
-namespace osu.Game.Screens.OnlinePlay.Components
+namespace osu.Game.Screens.OnlinePlay.Lounge
{
///
/// A that polls for the lounge listing.
///
- public partial class ListingPollingComponent : PollingComponent
+ public partial class LoungePollingComponent : PollingComponent
{
[Resolved]
private IAPIProvider api { get; set; } = null!;
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 2e78e88ccf..3a4da96ba1 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -24,7 +24,6 @@ using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets;
-using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
@@ -77,7 +76,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private readonly IBindable operationInProgress = new Bindable();
private readonly IBindable isIdle = new BindableBool();
private RoomsContainer roomsContainer = null!;
- private ListingPollingComponent listingPollingComponent = null!;
+ private LoungePollingComponent pollingComponent = null!;
private PopoverContainer popoverContainer = null!;
private LoadingLayer loadingLayer = null!;
private SearchTextBox searchTextBox = null!;
@@ -94,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
InternalChildren = new Drawable[]
{
- listingPollingComponent = new ListingPollingComponent
+ pollingComponent = new LoungePollingComponent
{
RoomsReceived = onListingReceived,
Filter = { BindTarget = filter }
@@ -189,7 +188,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
roomsContainer.Rooms.Clear();
hasListingResults.Value = false;
- listingPollingComponent.PollImmediately();
+ pollingComponent.PollImmediately();
});
updateFilter();
@@ -270,7 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
onReturning();
// Poll for any newly-created rooms (including potentially the user's own).
- listingPollingComponent.PollImmediately();
+ pollingComponent.PollImmediately();
}
public override bool OnExiting(ScreenExitEvent e)
@@ -388,7 +387,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected virtual void OpenNewRoom(Room room) => this.Push(CreateRoomSubScreen(room));
- public void RefreshRooms() => listingPollingComponent.PollImmediately();
+ public void RefreshRooms() => pollingComponent.PollImmediately();
private void updateLoadingLayer()
{
@@ -401,11 +400,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private void updatePollingRate(bool isCurrentScreen)
{
if (!isCurrentScreen)
- listingPollingComponent.TimeBetweenPolls.Value = 0;
+ pollingComponent.TimeBetweenPolls.Value = 0;
else
- listingPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
+ pollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
- Logger.Log($"Polling adjusted (listing: {listingPollingComponent.TimeBetweenPolls.Value})");
+ Logger.Log($"Polling adjusted (listing: {pollingComponent.TimeBetweenPolls.Value})");
}
protected abstract OsuButton CreateNewRoomButton();
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index bf0e428483..a74ae642fb 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private IdleTracker? idleTracker { get; set; }
private MatchLeaderboard leaderboard = null!;
- private SelectionPollingComponent selectionPollingComponent = null!;
+ private PlaylistsRoomUpdater roomUpdater = null!;
private FillFlowContainer progressSection = null!;
private DrawableRoomPlaylist drawablePlaylist = null!;
@@ -64,7 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (idleTracker != null)
isIdle.BindTo(idleTracker.IsIdle);
- AddInternal(selectionPollingComponent = new SelectionPollingComponent(Room));
+ AddInternal(roomUpdater = new PlaylistsRoomUpdater(Room));
}
protected override void LoadComplete()
@@ -328,8 +328,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
private void updatePollingRate()
{
- selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
- Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})");
+ roomUpdater.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
+ Logger.Log($"Polling adjusted (selection: {roomUpdater.TimeBetweenPolls.Value})");
}
private void closePlaylist()
diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomUpdater.cs
similarity index 88%
rename from osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomUpdater.cs
index bfa059f72e..f68703750a 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomUpdater.cs
@@ -7,19 +7,19 @@ using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
-namespace osu.Game.Screens.OnlinePlay.Components
+namespace osu.Game.Screens.OnlinePlay.Playlists
{
///
/// A that polls for and updates a room.
///
- public partial class SelectionPollingComponent : PollingComponent
+ public partial class PlaylistsRoomUpdater : PollingComponent
{
[Resolved]
private IAPIProvider api { get; set; } = null!;
private readonly Room room;
- public SelectionPollingComponent(Room room)
+ public PlaylistsRoomUpdater(Room room)
{
this.room = room;
}
From 205d6ecffbc989d75c1a32e53a29a9342b88c175 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Wed, 12 Feb 2025 22:51:25 +0900
Subject: [PATCH 027/349] Remove `SelectedRoom` abstraction from
`OnlinePlayTestScene`
---
.../StatefulMultiplayerClientTest.cs | 6 ++
.../TestSceneDrawableRoomParticipantsList.cs | 15 +++--
.../TestSceneLoungeRoomsContainer.cs | 7 +-
.../TestSceneMatchBeatmapDetailArea.cs | 10 +--
.../Multiplayer/TestSceneMatchLeaderboard.cs | 4 +-
.../TestSceneMultiSpectatorLeaderboard.cs | 2 +
.../TestSceneMultiSpectatorScreen.cs | 6 +-
.../TestSceneMultiplayerLoungeSubScreen.cs | 5 --
.../TestSceneMultiplayerMatchSongSelect.cs | 13 +++-
.../TestSceneMultiplayerMatchSubScreen.cs | 28 ++++----
.../TestSceneMultiplayerParticipantsList.cs | 6 +-
.../Multiplayer/TestSceneMultiplayerPlayer.cs | 6 ++
.../TestSceneMultiplayerPlaylist.cs | 5 +-
.../TestSceneMultiplayerQueueList.cs | 5 +-
.../TestSceneMultiplayerSpectateButton.cs | 11 +--
.../TestScenePlaylistsSongSelect.cs | 23 ++++---
.../TestScenePlaylistsMatchSettingsOverlay.cs | 29 ++++----
.../TestScenePlaylistsParticipantsList.cs | 10 +--
.../TestScenePlaylistsRoomCreation.cs | 12 ++--
.../Multiplayer/MultiplayerTestScene.cs | 67 ++++++++++---------
.../IOnlinePlayTestSceneDependencies.cs | 6 --
.../Visual/OnlinePlay/OnlinePlayTestScene.cs | 2 -
.../OnlinePlayTestSceneDependencies.cs | 4 --
23 files changed, 149 insertions(+), 133 deletions(-)
diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
index be30e06ed4..c0ca387260 100644
--- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
+++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs
@@ -15,6 +15,12 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
[HeadlessTest]
public partial class StatefulMultiplayerClientTest : MultiplayerTestScene
{
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+ JoinDefaultRoom();
+ }
+
[Test]
public void TestUserAddedOnJoin()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
index c1662bf944..2fd1268c8a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs
@@ -15,6 +15,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene
{
+ private Room room = null!;
private DrawableRoomParticipantsList list = null!;
public override void SetUpSteps()
@@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create list", () =>
{
- SelectedRoom.Value = new Room
+ room = new Room
{
Name = "test room",
Host = new APIUser
@@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
};
- Child = list = new DrawableRoomParticipantsList(SelectedRoom.Value)
+ Child = list = new DrawableRoomParticipantsList(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -119,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4);
AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46);
- AddStep("remove from end", () => removeUserAt(SelectedRoom.Value!.RecentParticipants.Count - 1));
+ AddStep("remove from end", () => removeUserAt(room.RecentParticipants.Count - 1));
AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4);
AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45);
@@ -138,18 +139,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void addUser(int id)
{
- SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Append(new APIUser
+ room.RecentParticipants = room.RecentParticipants.Append(new APIUser
{
Id = id,
Username = $"User {id}"
}).ToArray();
- SelectedRoom.Value!.ParticipantCount++;
+ room.ParticipantCount++;
}
private void removeUserAt(int index)
{
- SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value!.RecentParticipants[index])).ToArray();
- SelectedRoom.Value!.ParticipantCount--;
+ room.RecentParticipants = room.RecentParticipants.Where(u => !u.Equals(room.RecentParticipants[index])).ToArray();
+ room.ParticipantCount--;
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 772eb91174..e83a966144 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public partial class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
{
private BindableList rooms = null!;
+ private Bindable selectedRoom = null!;
private RoomsContainer container = null!;
public override void SetUpSteps()
@@ -30,6 +31,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create container", () =>
{
rooms = new BindableList();
+ selectedRoom = new Bindable();
+
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
@@ -40,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RelativeSizeAxes = Axes.Both,
Rooms = { BindTarget = rooms },
- SelectedRoom = { BindTarget = SelectedRoom }
+ SelectedRoom = { BindTarget = selectedRoom }
}
};
});
@@ -195,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, withPassword: true)));
}
- private bool checkRoomSelected(Room? room) => SelectedRoom.Value == room;
+ private bool checkRoomSelected(Room? room) => selectedRoom.Value == room;
private Room? getRoomInFlow(int index) =>
(container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
index 813a420cbd..e372d63fde 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs
@@ -16,15 +16,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene
{
+ private Room room = null!;
+
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("create area", () =>
{
- SelectedRoom.Value = new Room();
-
- Child = new MatchBeatmapDetailArea(SelectedRoom.Value)
+ Child = new MatchBeatmapDetailArea(room = new Room())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -36,9 +36,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void createNewItem()
{
- SelectedRoom.Value!.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ room.Playlist = room.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
- ID = SelectedRoom.Value.Playlist.Count,
+ ID = room.Playlist.Count,
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
index 38522db4d4..39ad21d0b0 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs
@@ -61,9 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create leaderboard", () =>
{
- SelectedRoom.Value = new Room { RoomID = 3 };
-
- Child = new MatchLeaderboard(SelectedRoom.Value)
+ Child = new MatchLeaderboard(new Room { RoomID = 3 })
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
index 3245b3c6a9..1821c2f3bc 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
+ JoinDefaultRoom();
+
AddStep("reset", () =>
{
leaderboard?.RemoveAndDisposeImmediately();
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
index 0a3d48828e..6cbd8a3fed 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs
@@ -17,6 +17,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
+using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
@@ -42,6 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager beatmapManager { get; set; } = null!;
private MultiSpectatorScreen spectatorScreen = null!;
+ private Room room = null!;
private readonly List playingUsers = new List();
@@ -63,6 +65,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
base.SetUpSteps();
AddStep("clear playing users", () => playingUsers.Clear());
+
+ JoinDefaultRoom(r => room = r);
}
[TestCase(1)]
@@ -455,7 +459,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
applyToBeatmap?.Invoke(Beatmap.Value);
- LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value!, playingUsers.ToArray()));
+ LoadScreen(spectatorScreen = new MultiSpectatorScreen(room, playingUsers.ToArray()));
});
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index b4ec9d5858..56187f8778 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -20,11 +20,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
private MultiplayerLoungeSubScreen loungeScreen = null!;
- public TestSceneMultiplayerLoungeSubScreen()
- : base(false)
- {
- }
-
public override void SetUpSteps()
{
base.SetUpSteps();
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
index 298e6e1b3c..287d7f5816 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
@@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private TestMultiplayerMatchSongSelect songSelect = null!;
private Live importedBeatmapSet = null!;
+ private Room room = null!;
[Resolved]
private OsuConfigManager configManager { get; set; } = null!;
@@ -58,6 +59,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
Add(beatmapStore);
}
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+ JoinDefaultRoom(r => room = r);
+ }
+
private void setUp()
{
AddStep("create song select", () =>
@@ -66,7 +73,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Beatmap.SetDefault();
SelectedMods.SetDefault();
- LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value!));
+ LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(room));
});
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
@@ -138,8 +145,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create song select", () =>
{
- SelectedRoom.Value!.Playlist.Single().RulesetID = 2;
- songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value, SelectedRoom.Value.Playlist.Single());
+ room.Playlist.Single().RulesetID = 2;
+ songSelect = new TestMultiplayerMatchSongSelect(room, room.Playlist.Single());
songSelect.OnLoadComplete += _ => Ruleset.Value = new TaikoRuleset().RulesetInfo;
LoadScreen(songSelect);
});
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
index e95209f993..18e926ca5d 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
@@ -43,11 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private MultiplayerMatchSubScreen screen = null!;
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
-
- public TestSceneMultiplayerMatchSubScreen()
- : base(false)
- {
- }
+ private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -66,8 +62,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("load match", () =>
{
- SelectedRoom.Value = new Room { Name = "Test Room" };
- LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value!));
+ room = new Room { Name = "Test Room" };
+ LoadScreen(screen = new TestMultiplayerMatchSubScreen(room));
});
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
@@ -78,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -97,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
{
@@ -122,7 +118,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set playlist", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -139,7 +135,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("set playlist", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
@@ -170,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with allowed mod", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -199,7 +195,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with allowed mod", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -223,7 +219,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item with no allowed mods", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
@@ -246,7 +242,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add two playlist items", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
@@ -285,7 +281,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value!.Playlist =
+ room.Playlist =
[
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
index 238a716f91..e7e6112297 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs
@@ -25,9 +25,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
{
- [SetUpSteps]
- public void SetupSteps()
+ public override void SetUpSteps()
{
+ base.SetUpSteps();
+
+ JoinDefaultRoom();
createNewParticipantsList();
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
index 94dd114c32..1a5be48cad 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
@@ -22,6 +22,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
private MultiplayerPlayer player = null!;
+ public override void SetUpSteps()
+ {
+ base.SetUpSteps();
+ JoinDefaultRoom();
+ }
+
[Test]
public void TestGameplay()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
index 77b75f407b..406c6cacae 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
private BeatmapInfo importedBeatmap = null!;
+ private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -46,9 +47,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
+ JoinDefaultRoom(r => room = r);
+
AddStep("create list", () =>
{
- Child = list = new MultiplayerPlaylist(SelectedRoom.Value!)
+ Child = list = new MultiplayerPlaylist(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
index 3ef2e4ecf4..5eba67bab5 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
@@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
private BeatmapInfo importedBeatmap = null!;
+ private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -42,9 +43,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
+ JoinDefaultRoom(r => room = r);
+
AddStep("create playlist", () =>
{
- Child = playlist = new MultiplayerQueueList(SelectedRoom.Value!)
+ Child = playlist = new MultiplayerQueueList(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
index 1429f86164..f92721b04b 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
@@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
private MultiplayerSpectateButton spectateButton = null!;
private MatchStartControl startControl = null!;
+ private Room room = null!;
private BeatmapSetInfo importedSet = null!;
private BeatmapManager beatmaps = null!;
@@ -46,11 +47,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
+ JoinDefaultRoom(r => room = r);
+
AddStep("create button", () =>
{
- PlaylistItem item = SelectedRoom.Value!.Playlist.First();
-
- AvailabilityTracker.SelectedItem.Value = item;
+ AvailabilityTracker.SelectedItem.Value = room.Playlist.First();
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
@@ -69,14 +70,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200, 50),
- SelectedItem = new Bindable(item)
+ SelectedItem = new Bindable(room.Playlist.First())
},
startControl = new MatchStartControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200, 50),
- SelectedItem = new Bindable(item)
+ SelectedItem = new Bindable(room.Playlist.First())
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index 726d0ac9f9..7c73fb8321 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
private BeatmapManager manager = null!;
private TestPlaylistsSongSelect songSelect = null!;
+ private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -51,13 +52,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("reset", () =>
{
- SelectedRoom.Value = new Room();
+ room = new Room();
Ruleset.Value = new OsuRuleset().RulesetInfo;
Beatmap.SetDefault();
SelectedMods.Value = Array.Empty();
});
- AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value!)));
+ AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(room)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
}
@@ -65,14 +66,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestItemAddedIfEmptyOnStart()
{
AddStep("finalise selection", () => songSelect.FinaliseSelection());
- AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
+ AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
}
[Test]
public void TestItemAddedWhenCreateNewItemClicked()
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
- AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
+ AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
}
[Test]
@@ -80,7 +81,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("finalise selection", () => songSelect.FinaliseSelection());
- AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
+ AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
}
[Test]
@@ -88,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
- AddAssert("playlist has 2 items", () => SelectedRoom.Value!.Playlist.Count == 2);
+ AddAssert("playlist has 2 items", () => room.Playlist.Count == 2);
}
[Test]
@@ -96,10 +97,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
- AddStep("rearrange", () => SelectedRoom.Value!.Playlist = SelectedRoom.Value!.Playlist.Skip(1).Append(SelectedRoom.Value!.Playlist[0]).ToArray());
+ AddStep("rearrange", () => room.Playlist = room.Playlist.Skip(1).Append(room.Playlist[0]).ToArray());
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
- AddAssert("new item has id 2", () => SelectedRoom.Value!.Playlist.Last().ID == 2);
+ AddAssert("new item has id 2", () => room.Playlist.Last().ID == 2);
}
///
@@ -115,13 +116,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item 1 has rate 1.5", () =>
{
- var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ var mod = (OsuModDoubleTime)room.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
});
AddAssert("item 2 has rate 2", () =>
{
- var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
+ var mod = (OsuModDoubleTime)room.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(2, mod.SpeedChange.Value);
});
}
@@ -147,7 +148,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
AddAssert("item has rate 1.5", () =>
{
- var m = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ var m = (OsuModDoubleTime)room.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
});
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index f7b0bc0d58..c714c39e22 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -18,6 +18,7 @@ namespace osu.Game.Tests.Visual.Playlists
public partial class TestScenePlaylistsMatchSettingsOverlay : OnlinePlayTestScene
{
private TestRoomSettings settings = null!;
+ private Room room = null!;
private Func? handleRequest;
public override void SetUpSteps()
@@ -47,9 +48,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create overlay", () =>
{
- SelectedRoom.Value = new Room();
-
- Child = settings = new TestRoomSettings(SelectedRoom.Value!)
+ Child = settings = new TestRoomSettings(room = new Room())
{
RelativeSizeAxes = Axes.Both,
State = { Value = Visibility.Visible }
@@ -62,19 +61,19 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("clear name and beatmap", () =>
{
- SelectedRoom.Value!.Name = "";
- SelectedRoom.Value!.Playlist = [];
+ room.Name = "";
+ room.Playlist = [];
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set name", () => SelectedRoom.Value!.Name = "Room name");
+ AddStep("set name", () => room.Name = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set beatmap", () => SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
+ AddStep("set beatmap", () => room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
- AddStep("clear name", () => SelectedRoom.Value!.Name = "");
+ AddStep("clear name", () => room.Name = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
}
@@ -90,7 +89,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
settings.NameField.Current.Value = expected_name;
settings.DurationField.Current.Value = expectedDuration;
- SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
+ room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
handleRequest = r =>
{
@@ -115,8 +114,8 @@ namespace osu.Game.Tests.Visual.Playlists
{
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
- SelectedRoom.Value!.Name = "Test Room";
- SelectedRoom.Value!.Playlist = [new PlaylistItem(beatmap)];
+ room.Name = "Test Room";
+ room.Playlist = [new PlaylistItem(beatmap)];
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
@@ -124,13 +123,13 @@ namespace osu.Game.Tests.Visual.Playlists
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
- AddAssert("playlist item valid", () => SelectedRoom.Value!.Playlist[0].Valid.Value);
+ AddAssert("playlist item valid", () => room.Playlist[0].Valid.Value);
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
AddAssert("error displayed", () => settings.ErrorText.IsPresent);
AddAssert("error has custom text", () => settings.ErrorText.Text != errorMessage);
- AddAssert("playlist item marked invalid", () => !SelectedRoom.Value!.Playlist[0].Valid.Value);
+ AddAssert("playlist item marked invalid", () => !room.Playlist[0].Valid.Value);
}
[Test]
@@ -142,8 +141,8 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("setup", () =>
{
- SelectedRoom.Value!.Name = "Test Room";
- SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
+ room.Name = "Test Room";
+ room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
handleRequest = _ => failText;
});
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs
index c60b208ffc..e1ec30d02a 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs
@@ -14,13 +14,15 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsParticipantsList : OnlinePlayTestScene
{
+ private Room room = null!;
+
public override void SetUpSteps()
{
base.SetUpSteps();
- AddStep("create list", () =>
+ AddStep("create room", () =>
{
- SelectedRoom.Value = new Room
+ room = new Room
{
RoomID = 7,
RecentParticipants = Enumerable.Range(0, 50).Select(_ => new APIUser
@@ -38,7 +40,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
- Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Horizontal)
+ Child = new ParticipantsDisplay(room, Direction.Horizontal)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -52,7 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
- Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Vertical)
+ Child = new ParticipantsDisplay(room, Direction.Vertical)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
index 0270840597..a748d61d44 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
@@ -35,6 +35,7 @@ namespace osu.Game.Tests.Visual.Playlists
private BeatmapManager manager = null!;
private TestPlaylistsRoomSubScreen match = null!;
private BeatmapSetInfo importedBeatmap = null!;
+ private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -47,11 +48,9 @@ namespace osu.Game.Tests.Visual.Playlists
[SetUpSteps]
public void SetupSteps()
{
- AddStep("set room", () => SelectedRoom.Value = new Room());
-
importBeatmap();
- AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value!)));
+ AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(room = new Room())));
AddUntilStep("wait for load", () => match.IsCurrentScreen());
}
@@ -119,7 +118,7 @@ namespace osu.Game.Tests.Visual.Playlists
];
});
- AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value!.Playlist[0]);
+ AddAssert("first playlist item selected", () => match.SelectedItem.Value == room.Playlist[0]);
}
[Test]
@@ -197,10 +196,9 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("match has correct beatmap", () => realHash == match.Beatmap.Value.BeatmapInfo.MD5Hash);
}
- private void setupAndCreateRoom(Action room)
+ private void setupAndCreateRoom(Action setupFunc)
{
- AddStep("setup room", () => room(SelectedRoom.Value!));
-
+ AddStep("setup room", () => setupFunc(room));
AddStep("click create button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType().Single());
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
index d1497d5142..97c213c7b1 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.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 osu.Game.Online.Rooms;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay;
@@ -23,43 +24,43 @@ namespace osu.Game.Tests.Visual.Multiplayer
public bool RoomJoined => MultiplayerClient.RoomJoined;
- private readonly bool joinRoom;
-
- protected MultiplayerTestScene(bool joinRoom = true)
+ ///
+ /// Creates and joins a basic multiplayer room.
+ ///
+ /// A callback that may be used to further set up the room.
+ protected void JoinDefaultRoom(Action? setupFunc = null)
{
- this.joinRoom = joinRoom;
- }
-
- protected virtual Room CreateRoom()
- {
- return new Room
+ AddStep("join room", () =>
{
- Name = "test name",
- Type = MatchType.HeadToHead,
- Playlist =
- [
- new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
- {
- RulesetID = Ruleset.Value.OnlineID
- }
- ]
- };
- }
-
- public override void SetUpSteps()
- {
- base.SetUpSteps();
-
- if (joinRoom)
- {
- AddStep("join room", () =>
+ Room room = new Room
{
- SelectedRoom.Value = CreateRoom();
- MultiplayerClient.CreateRoom(SelectedRoom.Value).ConfigureAwait(false);
- });
+ Name = "test name",
+ Type = MatchType.HeadToHead,
+ Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
+ {
+ RulesetID = Ruleset.Value.OnlineID
+ }
+ ]
+ };
- AddUntilStep("wait for room join", () => RoomJoined);
- }
+ setupFunc?.Invoke(room);
+
+ MultiplayerClient.CreateRoom(room).ConfigureAwait(false);
+ });
+
+ AddUntilStep("wait for room join", () => RoomJoined);
+ }
+
+ ///
+ /// Creates and joins the given room.
+ ///
+ /// The room to create. If null, a default room will be created.
+ protected void JoinRoom(Room room)
+ {
+ AddStep("join room", () => MultiplayerClient.CreateRoom(room).ConfigureAwait(false));
+ AddUntilStep("wait for room join", () => RoomJoined);
}
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
index 5780cf6eff..60730ee9a4 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.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.Bindables;
using osu.Game.Database;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay;
@@ -13,11 +12,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
public interface IOnlinePlayTestSceneDependencies
{
- ///
- /// The cached .
- ///
- Bindable SelectedRoom { get; }
-
///
/// The cached .
///
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
index c3a5e1c3ec..ce8df36590 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs
@@ -3,7 +3,6 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
@@ -22,7 +21,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlayTestSceneDependencies
{
- public Bindable SelectedRoom => OnlinePlayDependencies.SelectedRoom;
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies.OngoingOperationTracker;
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies.AvailabilityTracker;
public TestUserLookupCache UserLookupCache => OnlinePlayDependencies.UserLookupCache;
diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
index cc448beea0..9537c7958c 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Database;
using osu.Game.Online.Rooms;
@@ -18,7 +17,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
///
public class OnlinePlayTestSceneDependencies : IReadOnlyDependencyContainer, IOnlinePlayTestSceneDependencies
{
- public Bindable SelectedRoom { get; }
public OngoingOperationTracker OngoingOperationTracker { get; }
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
public TestRoomRequestsHandler RequestsHandler { get; }
@@ -35,7 +33,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
public OnlinePlayTestSceneDependencies()
{
- SelectedRoom = new Bindable();
RequestsHandler = new TestRoomRequestsHandler();
OngoingOperationTracker = new OngoingOperationTracker();
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
@@ -45,7 +42,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
dependencies = new DependencyContainer();
CacheAs(RequestsHandler);
- CacheAs(SelectedRoom);
CacheAs(OngoingOperationTracker);
CacheAs(AvailabilityTracker);
CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum));
From d923a478e9a044432cd611424ff57b5862d69865 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 13 Feb 2025 00:04:33 +0900
Subject: [PATCH 028/349] Remove unused method
---
.../Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
index 97c213c7b1..8150807f4f 100644
--- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
+++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs
@@ -53,16 +53,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for room join", () => RoomJoined);
}
- ///
- /// Creates and joins the given room.
- ///
- /// The room to create. If null, a default room will be created.
- protected void JoinRoom(Room room)
- {
- AddStep("join room", () => MultiplayerClient.CreateRoom(room).ConfigureAwait(false));
- AddUntilStep("wait for room join", () => RoomJoined);
- }
-
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
}
}
From d930da62104bb2c65fede2771d9a670524b80c60 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Thu, 13 Feb 2025 18:10:19 +0900
Subject: [PATCH 029/349] Rewrite playlists to not inherit `RoomSubScreen`
---
.../OnlinePlay/Lounge/LoungeSubScreen.cs | 3 +-
.../Multiplayer/MultiplayerLoungeSubScreen.cs | 3 +-
.../Playlists/PlaylistsLoungeSubScreen.cs | 3 +-
.../Playlists/PlaylistsRoomSubScreen.cs | 955 +++++++++++++-----
4 files changed, 716 insertions(+), 248 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index f00cf7427c..c3b3d63dcb 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -26,7 +26,6 @@ using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
-using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
using osuTK;
@@ -408,7 +407,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
/// The created .
protected abstract Room CreateNewRoom();
- protected abstract RoomSubScreen CreateRoomSubScreen(Room room);
+ protected abstract OnlinePlaySubScreen CreateRoomSubScreen(Room room);
protected abstract ListingPollingComponent CreatePollingComponent();
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index dd61caa3db..1d728998b9 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -17,7 +17,6 @@ using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
-using osu.Game.Screens.OnlinePlay.Match;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
@@ -89,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Type = MatchType.HeadToHead,
};
- protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
+ protected override OnlinePlaySubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
protected override ListingPollingComponent CreatePollingComponent() => new MultiplayerListingPollingComponent();
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index d66b4f844c..8670fcf78f 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -13,7 +13,6 @@ using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
-using osu.Game.Screens.OnlinePlay.Match;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
@@ -70,7 +69,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
};
}
- protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
+ protected override OnlinePlaySubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
protected override ListingPollingComponent CreatePollingComponent() => new ListingPollingComponent();
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 7f2255e482..22b9006e47 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -2,60 +2,126 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Framework.Screens;
+using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
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;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Match.Components;
+using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users;
+using osu.Game.Utils;
using osuTK;
using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
- public partial class PlaylistsRoomSubScreen : RoomSubScreen
+ public partial class PlaylistsRoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
{
public override string Title { get; }
public override string ShortTitle => "playlist";
- private readonly IBindable isIdle = new BindableBool();
+ public override bool? ApplyModTrackAdjustments => true;
+
+ public override bool DisallowExternalBeatmapRulesetChanges => true;
+
+ ///
+ /// Whether the user has confirmed they want to exit this screen in the presence of unsaved changes.
+ ///
+ protected bool ExitConfirmed { get; private set; }
[Resolved]
private IAPIProvider api { get; set; } = null!;
- [Resolved(CanBeNull = true)]
+ [Resolved]
+ private AudioManager audio { get; set; } = null!;
+
+ [Resolved]
+ private BeatmapManager beatmapManager { get; set; } = null!;
+
+ [Resolved]
+ private RulesetStore rulesets { get; set; } = null!;
+
+ [Resolved]
+ private PreviewTrackManager previewTrackManager { get; set; } = null!;
+
+ [Resolved]
+ private MusicController music { get; set; } = null!;
+
+ [Resolved]
private IdleTracker? idleTracker { get; set; }
- private MatchLeaderboard leaderboard = null!;
+ [Resolved]
+ private OnlinePlayScreen? parentScreen { get; set; }
+
+ [Resolved]
+ private IOverlayManager? overlayManager { get; set; }
+
+ [Resolved]
+ private IDialogOverlay? dialogOverlay { get; set; }
+
+ [Cached]
+ private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
+
+ protected readonly Bindable SelectedItem = new Bindable();
+ private readonly Bindable userBeatmap = new Bindable();
+ private readonly Bindable userRuleset = new Bindable();
+ private readonly Bindable> userMods = new Bindable>(Array.Empty());
+
+ private readonly IBindable isIdle = new BindableBool();
+ private readonly Room room;
+
+ private Drawable roomContent = null!;
private SelectionPollingComponent selectionPollingComponent = null!;
+ private PlaylistsRoomSettingsOverlay settingsOverlay = null!;
+
+ private MatchLeaderboard leaderboard = null!;
private FillFlowContainer progressSection = null!;
private DrawableRoomPlaylist drawablePlaylist = null!;
- private readonly Bindable userBeatmap = new Bindable();
- private readonly Bindable userRuleset = new Bindable();
+ private FillFlowContainer userModsSection = null!;
+ private RoomModSelectOverlay userModsSelectOverlay = null!;
+
+ private FillFlowContainer userStyleSection = null!;
+ private Container userStyleDisplayContainer = null!;
+
+ private Sample? sampleStart;
+ private IDisposable? userModsSelectOverlayRegistration;
public PlaylistsRoomSubScreen(Room room)
- : base(room, false) // Editing is temporarily not allowed.
{
+ this.room = room;
+
Title = room.RoomID == null ? "New playlist" : room.Name;
Activity.Value = new UserActivity.InLobby(room);
+
+ Padding = new MarginPadding { Top = Header.HEIGHT };
}
[BackgroundDependencyLoader]
@@ -64,32 +130,350 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (idleTracker != null)
isIdle.BindTo(idleTracker.IsIdle);
- AddInternal(selectionPollingComponent = new SelectionPollingComponent(Room));
+ sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection");
+
+ InternalChild = new PopoverContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ selectionPollingComponent = new SelectionPollingComponent(room),
+ beatmapAvailabilityTracker,
+ new MultiplayerRoomSounds(),
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ RowDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 50)
+ },
+ Content = new[]
+ {
+ // Padded main content (drawable room + main content)
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding
+ {
+ Horizontal = WaveOverlayContainer.WIDTH_PADDING,
+ Bottom = 30
+ },
+ Children = new[]
+ {
+ roomContent = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, 10)
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new OsuContextMenuContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Child = new DrawableMatchRoom(room, false)
+ {
+ OnEdit = () => settingsOverlay.Show(),
+ SelectedItem = SelectedItem
+ }
+ }
+ },
+ null,
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = 10,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary.
+ },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding(20),
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
+ Child = new OsuContextMenuContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ },
+ Content = new[]
+ {
+ new Drawable?[]
+ {
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = 5 },
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedPlaylistHeader(room), },
+ new Drawable[]
+ {
+ drawablePlaylist = new DrawableRoomPlaylist
+ {
+ RelativeSizeAxes = Axes.Both,
+ SelectedItem = { BindTarget = SelectedItem },
+ AllowSelection = true,
+ AllowShowingResults = true,
+ RequestResults = item =>
+ {
+ Debug.Assert(room.RoomID != null);
+ parentScreen?.Push(new PlaylistItemUserBestResultsScreen(room.RoomID.Value, item,
+ api.LocalUser.Value.Id));
+ }
+ }
+ },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ },
+ null,
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ userModsSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Bottom = 10 },
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Extra mods"),
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new UserModSelectButton
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 90,
+ Height = 30,
+ Text = "Select",
+ Action = showUserModSelect,
+ },
+ new ModDisplay
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Current = userMods,
+ Scale = new Vector2(0.8f),
+ },
+ }
+ }
+ }
+ },
+ },
+ new Drawable[]
+ {
+ userStyleSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Bottom = 10 },
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Difficulty"),
+ userStyleDisplayContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y
+ }
+ }
+ },
+ },
+ new Drawable[]
+ {
+ progressSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 10 },
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Progress"),
+ new RoomLocalUserInfo(room),
+ }
+ },
+ },
+ new Drawable[]
+ {
+ new OverlinedHeader("Leaderboard")
+ },
+ new Drawable[] { leaderboard = new MatchLeaderboard(room) { RelativeSizeAxes = Axes.Both }, },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ },
+ null,
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedHeader("Chat") },
+ new Drawable[] { new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both } }
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ },
+ },
+ },
+ }
+ }
+ }
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ },
+ }
+ }
+ }
+ }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ // Resolves 1px masking errors between the settings overlay and the room panel.
+ Padding = new MarginPadding(-1),
+ Child = settingsOverlay = new PlaylistsRoomSettingsOverlay(room)
+ {
+ EditPlaylist = () =>
+ {
+ if (this.IsCurrentScreen())
+ this.Push(new PlaylistsSongSelect(room));
+ },
+ }
+ }
+ },
+ },
+ },
+ // Footer
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex(@"28242d") // Temporary.
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding(5),
+ Child = new PlaylistsRoomFooter(room)
+ {
+ OnStart = startPlay,
+ OnClose = closePlaylist,
+ }
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ LoadComponent(userModsSelectOverlay = new RoomModSelectOverlay
+ {
+ SelectedItem = { BindTarget = SelectedItem },
+ SelectedMods = { BindTarget = userMods },
+ IsValidMod = _ => false
+ });
}
protected override void LoadComplete()
{
base.LoadComplete();
- SelectedItem.BindValueChanged(onSelectedItemChanged, true);
+ userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay);
+
+ room.PropertyChanged += onRoomPropertyChanged;
+
isIdle.BindValueChanged(_ => updatePollingRate(), true);
+ SelectedItem.BindValueChanged(_ => onSelectedItemChanged());
+
+ beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
+ beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateGameplayState());
+
+ userBeatmap.BindValueChanged(_ => updateGameplayState());
+ userRuleset.BindValueChanged(_ => updateGameplayState());
+ userMods.BindValueChanged(_ => updateGameplayState());
- Room.PropertyChanged += onRoomPropertyChanged;
updateSetupState();
- updateRoomMaxAttempts();
- updateRoomPlaylist();
+ updateGameplayState();
}
- private void onSelectedItemChanged(ValueChangedEvent item)
- {
- // Simplest for now.
- userBeatmap.Value = null;
- userRuleset.Value = null;
- }
-
- protected override IBeatmapInfo GetGameplayBeatmap() => userBeatmap.Value ?? base.GetGameplayBeatmap();
- protected override RulesetInfo GetGameplayRuleset() => userRuleset.Value ?? base.GetGameplayRuleset();
+ #region Room/property updates
+ ///
+ /// Responds to changes of the 's properties.
+ ///
+ /// The that changed.
+ /// Describes the property that changed.
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
@@ -97,255 +481,342 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
case nameof(Room.RoomID):
updateSetupState();
break;
-
- case nameof(Room.MaxAttempts):
- updateRoomMaxAttempts();
- break;
-
- case nameof(Room.Playlist):
- updateRoomPlaylist();
- break;
}
}
+ ///
+ /// Adjusts the visibility of the settings and main content when changes.
+ /// Only the settings overlay is visible while the room isn't created, and only the main content is visible after creation.
+ ///
private void updateSetupState()
{
- if (Room.RoomID != null)
+ if (room.RoomID == null)
{
- // Set the first playlist item.
- // This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
- Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault());
+ // A new room is being created.
+ // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
+ roomContent.Hide();
+ settingsOverlay.Show();
+ }
+ else
+ {
+ roomContent.Show();
+ settingsOverlay.Hide();
+
+ progressSection.Alpha = room.MaxAttempts != null ? 1 : 0;
+ drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
+ SelectedItem.Value = room.Playlist.FirstOrDefault();
}
}
- private void updateRoomMaxAttempts()
- => progressSection.Alpha = Room.MaxAttempts != null ? 1 : 0;
-
- private void updateRoomPlaylist()
- => drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, Room.Playlist);
-
- protected override Drawable CreateMainContent() => new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
- Child = new OsuContextMenuContainer
- {
- RelativeSizeAxes = Axes.Both,
- Child = new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- ColumnDimensions = new[]
- {
- new Dimension(),
- new Dimension(GridSizeMode.Absolute, 10),
- new Dimension(),
- new Dimension(GridSizeMode.Absolute, 10),
- new Dimension(),
- },
- Content = new[]
- {
- new Drawable?[]
- {
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = 5 },
- Content = new[]
- {
- new Drawable[] { new OverlinedPlaylistHeader(Room), },
- new Drawable[]
- {
- drawablePlaylist = new DrawableRoomPlaylist
- {
- RelativeSizeAxes = Axes.Both,
- SelectedItem = { BindTarget = SelectedItem },
- AllowSelection = true,
- AllowShowingResults = true,
- RequestResults = item =>
- {
- Debug.Assert(Room.RoomID != null);
- ParentScreen?.Push(new PlaylistItemUserBestResultsScreen(Room.RoomID.Value, item, api.LocalUser.Value.Id));
- }
- }
- },
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new[]
- {
- UserModsSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Bottom = 10 },
- Alpha = 0,
- Children = new Drawable[]
- {
- new OverlinedHeader("Extra mods"),
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(10, 0),
- Children = new Drawable[]
- {
- new UserModSelectButton
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Width = 90,
- Height = 30,
- Text = "Select",
- Action = ShowUserModSelect,
- },
- new ModDisplay
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Current = UserMods,
- Scale = new Vector2(0.8f),
- },
- }
- }
- }
- },
- },
- new[]
- {
- UserStyleSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Bottom = 10 },
- Alpha = 0,
- Children = new Drawable[]
- {
- new OverlinedHeader("Difficulty"),
- UserStyleDisplayContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y
- }
- }
- },
- },
- new Drawable[]
- {
- progressSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- new OverlinedHeader("Progress"),
- new RoomLocalUserInfo(Room),
- }
- },
- },
- new Drawable[]
- {
- new OverlinedHeader("Leaderboard")
- },
- new Drawable[] { leaderboard = new MatchLeaderboard(Room) { RelativeSizeAxes = Axes.Both }, },
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new Drawable[] { new OverlinedHeader("Chat") },
- new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } }
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- },
- },
- }
- }
- };
-
- protected override Drawable CreateFooter() => new PlaylistsRoomFooter(Room)
- {
- OnStart = StartPlay,
- OnClose = closePlaylist,
- };
-
- protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new PlaylistsRoomSettingsOverlay(room)
- {
- EditPlaylist = () =>
- {
- if (this.IsCurrentScreen())
- this.Push(new PlaylistsSongSelect(Room));
- },
- };
-
- protected override void OpenStyleSelection()
- {
- if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
- return;
-
- this.Push(new PlaylistsRoomFreestyleSelect(Room, item)
- {
- Beatmap = { BindTarget = userBeatmap },
- Ruleset = { BindTarget = userRuleset }
- });
- }
-
+ ///
+ /// Adjusts the rate at which the is updated.
+ ///
private void updatePollingRate()
{
selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})");
}
- private void closePlaylist()
+ ///
+ /// Responds to changes in the selected playlist item to validate the user's beatmap/ruleset/mod style and update UI components as necessary.
+ ///
+ private void onSelectedItemChanged()
{
- DialogOverlay?.Push(new ClosePlaylistDialog(Room, () =>
+ if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
+ return;
+
+ // Reset entire user style when disabled.
+ if (!item.Freestyle)
{
- var request = new ClosePlaylistRequest(Room.RoomID!.Value);
- request.Success += () => Room.EndDate = DateTimeOffset.UtcNow;
- API.Queue(request);
+ userBeatmap.Value = null;
+ userRuleset.Value = null;
+ }
+
+ // Reset beatmap style when no longer from the same beatmap set.
+ if (userBeatmap.Value != null && userBeatmap.Value.BeatmapSet!.OnlineID != item.Beatmap.BeatmapSet!.OnlineID)
+ userBeatmap.Value = null;
+
+ // Reset ruleset style when no longer valid for the beatmap.
+ if (userRuleset.Value != null)
+ {
+ IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
+ int beatmapRuleset = gameplayBeatmap.Ruleset.OnlineID;
+
+ if (beatmapRuleset > 0 && userRuleset.Value.OnlineID != beatmapRuleset)
+ userRuleset.Value = null;
+ }
+
+ RulesetInfo gameplayRuleset = userRuleset.Value ?? rulesets.GetRuleset(item.RulesetID)!;
+ Ruleset rulesetInstance = gameplayRuleset.CreateInstance();
+ Mod[] allowedMods = item.Freestyle
+ ? rulesetInstance.AllMods.OfType().Where(m => ModUtils.IsValidFreeModForMatchType(m, room.Type)).ToArray()
+ : item.AllowedMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
+
+ // Remove any user mods that are no longer allowed.
+ Mod[] newUserMods = userMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToArray();
+ if (!newUserMods.SequenceEqual(userMods.Value))
+ userMods.Value = newUserMods;
+
+ if (allowedMods.Length > 0)
+ {
+ userModsSection.Show();
+ userModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
+ }
+ else
+ {
+ userModsSection.Hide();
+ userModsSelectOverlay.Hide();
+ userModsSelectOverlay.IsValidMod = _ => false;
+ }
+
+ if (item.Freestyle)
+ userStyleSection.Show();
+ else
+ userStyleSection.Hide();
+
+ updateGameplayState();
+ }
+
+ ///
+ /// Adjusts the global beatmap/ruleset/mods values in preparation for a gameplay session.
+ ///
+ private void updateGameplayState()
+ {
+ if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
+ return;
+
+ IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
+ RulesetInfo gameplayRuleset = userRuleset.Value ?? rulesets.GetRuleset(item.RulesetID)!;
+ Ruleset rulesetInstance = gameplayRuleset.CreateInstance();
+ Mod[] gameplayMods = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToArray();
+
+ // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
+ int beatmapId = gameplayBeatmap.OnlineID;
+ var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == beatmapId);
+
+ Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
+ Ruleset.Value = gameplayRuleset;
+ Mods.Value = gameplayMods;
+
+ if (item.Freestyle)
+ {
+ PlaylistItem gameplayItem = item.With(ruleset: gameplayRuleset.OnlineID, beatmap: new Optional(gameplayBeatmap));
+ PlaylistItem? currentItem = userStyleDisplayContainer.SingleOrDefault()?.Item;
+
+ if (!gameplayItem.Equals(currentItem))
+ {
+ userStyleDisplayContainer.Child = new DrawableRoomPlaylistItem(gameplayItem, true)
+ {
+ AllowReordering = false,
+ AllowEditing = true,
+ RequestEdit = _ => showUserStyleSelect()
+ };
+ }
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Pushes a to start gameplay with the current selection.
+ ///
+ private void startPlay()
+ {
+ if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
+ return;
+
+ // Required for validation inside the player.
+ RulesetInfo gameplayRuleset = userRuleset.Value ?? rulesets.GetRuleset(item.RulesetID)!;
+ IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
+ PlaylistItem gameplayItem = item.With(ruleset: gameplayRuleset.OnlineID, beatmap: new Optional(gameplayBeatmap));
+
+ sampleStart?.Play();
+
+ // fallback is to allow this class to operate when there is no parent OnlineScreen (testing purposes).
+ var targetScreen = (Screen?)parentScreen ?? this;
+ targetScreen.Push(new PlayerLoader(() => new PlaylistsPlayer(room, gameplayItem)
+ {
+ Exited = () => leaderboard.RefetchScores()
}));
}
- protected override Screen CreateGameplayScreen(PlaylistItem selectedItem)
+ ///
+ /// Shows the user mod selection.
+ ///
+ private void showUserModSelect()
{
- return new PlayerLoader(() => new PlaylistsPlayer(Room, selectedItem)
+ if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem)
+ return;
+
+ userModsSelectOverlay.Show();
+ }
+
+ ///
+ /// Shows the user style selection.
+ ///
+ private void showUserStyleSelect()
+ {
+ if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
+ return;
+
+ this.Push(new PlaylistsRoomFreestyleSelect(room, item)
{
- Exited = () => leaderboard.RefetchScores()
+ Beatmap = { BindTarget = userBeatmap },
+ Ruleset = { BindTarget = userRuleset }
});
}
+ ///
+ /// May be invoked by the owner of the room to permanently close the room ahead of its intended end date.
+ ///
+ private void closePlaylist()
+ {
+ dialogOverlay?.Push(new ClosePlaylistDialog(room, () =>
+ {
+ var request = new ClosePlaylistRequest(room.RoomID!.Value);
+ request.Success += () => room.EndDate = DateTimeOffset.UtcNow;
+ api.Queue(request);
+ }));
+ }
+
+ #region Screen transition / track handling
+
+ public override void OnEntering(ScreenTransitionEvent e)
+ {
+ base.OnEntering(e);
+ updateGameplayState();
+ beginHandlingTrack();
+ }
+
+ public override void OnSuspending(ScreenTransitionEvent e)
+ {
+ endHandlingTrack();
+ base.OnSuspending(e);
+ }
+
+ public override void OnResuming(ScreenTransitionEvent e)
+ {
+ base.OnResuming(e);
+ updateGameplayState();
+ beginHandlingTrack();
+ }
+
+ public override bool OnExiting(ScreenExitEvent e)
+ {
+ if (!ensureExitConfirmed())
+ return true;
+
+ RoomManager?.PartRoom();
+
+ endHandlingTrack();
+ return base.OnExiting(e);
+ }
+
+ public override bool OnBackButton()
+ {
+ if (room.RoomID == null)
+ {
+ if (!ensureExitConfirmed())
+ return true;
+
+ settingsOverlay.Hide();
+ return base.OnBackButton();
+ }
+
+ if (userModsSelectOverlay.State.Value == Visibility.Visible)
+ {
+ userModsSelectOverlay.Hide();
+ return true;
+ }
+
+ if (settingsOverlay.State.Value == Visibility.Visible)
+ {
+ settingsOverlay.Hide();
+ return true;
+ }
+
+ return base.OnBackButton();
+ }
+
+ ///
+ /// Handles changes in the track to keep it looping while active.
+ ///
+ private void beginHandlingTrack()
+ {
+ Beatmap.BindValueChanged(applyLoopingToTrack, true);
+ }
+
+ ///
+ /// Stops looping the current track and stops handling further changes to the track.
+ ///
+ private void endHandlingTrack()
+ {
+ Beatmap.ValueChanged -= applyLoopingToTrack;
+ Beatmap.Value.Track.Looping = false;
+
+ previewTrackManager.StopAnyPlaying(this);
+ }
+
+ ///
+ /// Invoked on changes to the beatmap to loop the track. See: .
+ ///
+ /// The beatmap change event.
+ private void applyLoopingToTrack(ValueChangedEvent beatmap)
+ {
+ if (!this.IsCurrentScreen())
+ return;
+
+ beatmap.NewValue.PrepareTrackForPreview(true);
+ music.EnsurePlayingSomething();
+ }
+
+ ///
+ /// Prompts the user to discard unsaved changes to the room before exiting.
+ ///
+ /// true if the user has confirmed they want to exit.
+ private bool ensureExitConfirmed()
+ {
+ if (ExitConfirmed)
+ return true;
+
+ if (api.State.Value == APIState.Online)
+ return true;
+
+ bool hasUnsavedChanges = room.RoomID == null && room.Playlist.Count > 0;
+
+ if (dialogOverlay == null || !hasUnsavedChanges)
+ return true;
+
+ // if the dialog is already displayed, block exiting until the user explicitly makes a decision.
+ if (dialogOverlay.CurrentDialog is ConfirmDiscardChangesDialog discardChangesDialog)
+ {
+ discardChangesDialog.Flash();
+ return false;
+ }
+
+ dialogOverlay.Push(new ConfirmDiscardChangesDialog(() =>
+ {
+ ExitConfirmed = true;
+ settingsOverlay.Hide();
+ this.Exit();
+ }));
+
+ return false;
+ }
+
+ #endregion
+
+ protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(room.Playlist.FirstOrDefault())
+ {
+ SelectedItem = { BindTarget = SelectedItem }
+ };
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
- Room.PropertyChanged -= onRoomPropertyChanged;
+
+ userModsSelectOverlayRegistration?.Dispose();
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
From 1cb4956cacd6e677af269419ba64e7fdadbf6f65 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 14 Feb 2025 20:30:54 +0900
Subject: [PATCH 030/349] Fix not properly selecting the first playlist item
---
.../Playlists/PlaylistsRoomSubScreen.cs | 24 +++++++++----------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 22b9006e47..76f0c04295 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -467,8 +467,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
updateGameplayState();
}
- #region Room/property updates
-
///
/// Responds to changes of the 's properties.
///
@@ -485,7 +483,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
///
- /// Adjusts the visibility of the settings and main content when changes.
+ /// Responds to changes in to adjust the visibility of the settings and main content.
/// Only the settings overlay is visible while the room isn't created, and only the main content is visible after creation.
///
private void updateSetupState()
@@ -502,9 +500,15 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
roomContent.Show();
settingsOverlay.Hide();
- progressSection.Alpha = room.MaxAttempts != null ? 1 : 0;
- drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
- SelectedItem.Value = room.Playlist.FirstOrDefault();
+ // Scheduled because room properties are updated in arbitrary order.
+ Schedule(() =>
+ {
+ progressSection.Alpha = room.MaxAttempts != null ? 1 : 0;
+ drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
+
+ // Select an initial item for the user to help them get into a playable state quicker.
+ SelectedItem.Value = room.Playlist.FirstOrDefault();
+ });
}
}
@@ -518,7 +522,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
///
- /// Responds to changes in the selected playlist item to validate the user's beatmap/ruleset/mod style and update UI components as necessary.
+ /// Responds to changes in the selected playlist item to validate the user's style selection.
///
private void onSelectedItemChanged()
{
@@ -615,8 +619,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
}
- #endregion
-
///
/// Pushes a to start gameplay with the current selection.
///
@@ -679,8 +681,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}));
}
- #region Screen transition / track handling
-
public override void OnEntering(ScreenTransitionEvent e)
{
base.OnEntering(e);
@@ -804,8 +804,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
return false;
}
- #endregion
-
protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(room.Playlist.FirstOrDefault())
{
SelectedItem = { BindTarget = SelectedItem }
From 9458f0d01d2ae41d980a8a7a6a4bdc972e09457c Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 14 Feb 2025 20:38:44 +0900
Subject: [PATCH 031/349] Remove unnecessary update, document other usage
---
.../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 76f0c04295..45e220cce9 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -684,7 +684,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public override void OnEntering(ScreenTransitionEvent e)
{
base.OnEntering(e);
- updateGameplayState();
beginHandlingTrack();
}
@@ -697,8 +696,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public override void OnResuming(ScreenTransitionEvent e)
{
base.OnResuming(e);
- updateGameplayState();
beginHandlingTrack();
+
+ // Required when resuming from style selection.
+ updateGameplayState();
}
public override bool OnExiting(ScreenExitEvent e)
From e17383edbdfc6bac0c14d8ab1cdc6eea2d7d8045 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 14 Feb 2025 21:47:23 +0900
Subject: [PATCH 032/349] Rewrite/optimise layout
Reducing the amount of nesting to make things more readable.
---
.../Playlists/PlaylistsRoomSubScreen.cs | 496 +++++++++---------
1 file changed, 243 insertions(+), 253 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 45e220cce9..87bec5d8e1 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -13,7 +13,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Framework.Screens;
@@ -43,6 +42,31 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
public partial class PlaylistsRoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
{
+ ///
+ /// Footer height.
+ ///
+ private const float footer_height = 50;
+
+ ///
+ /// Padding between content and footer.
+ ///
+ private const float footer_padding = 30;
+
+ ///
+ /// Internal padding of the content.
+ ///
+ private const float content_padding = 20;
+
+ ///
+ /// Padding between columns of the content.
+ ///
+ private const float column_padding = 10;
+
+ ///
+ /// Padding between rows of the content.
+ ///
+ private const float row_padding = 10;
+
public override string Title { get; }
public override string ShortTitle => "playlist";
@@ -132,7 +156,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection");
- InternalChild = new PopoverContainer
+ InternalChild = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
@@ -140,297 +164,263 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
selectionPollingComponent = new SelectionPollingComponent(room),
beatmapAvailabilityTracker,
new MultiplayerRoomSounds(),
- new GridContainer
+ new Container
{
RelativeSizeAxes = Axes.Both,
- RowDimensions = new[]
+ Padding = new MarginPadding
{
- new Dimension(),
- new Dimension(GridSizeMode.Absolute, 50)
+ Horizontal = WaveOverlayContainer.WIDTH_PADDING,
+ Bottom = footer_height + footer_padding
},
- Content = new[]
+ Children = new[]
{
- // Padded main content (drawable room + main content)
- new Drawable[]
+ roomContent = new GridContainer
{
- new Container
+ RelativeSizeAxes = Axes.Both,
+ RowDimensions = new[]
{
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, row_padding),
+ },
+ Content = new[]
+ {
+ new Drawable[]
{
- Horizontal = WaveOverlayContainer.WIDTH_PADDING,
- Bottom = 30
+ new DrawableMatchRoom(room, false)
+ {
+ OnEdit = () => settingsOverlay.Show(),
+ SelectedItem = SelectedItem
+ }
},
- Children = new[]
+ null,
+ new Drawable[]
{
- roomContent = new GridContainer
+ new Container
{
RelativeSizeAxes = Axes.Both,
- RowDimensions = new[]
+ Masking = true,
+ CornerRadius = 10,
+ Children = new Drawable[]
{
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Absolute, 10)
- },
- Content = new[]
- {
- new Drawable[]
+ new Box
{
- new OsuContextMenuContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Child = new DrawableMatchRoom(room, false)
- {
- OnEdit = () => settingsOverlay.Show(),
- SelectedItem = SelectedItem
- }
- }
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary.
},
- null,
- new Drawable[]
+ new GridContainer
{
- new Container
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding(content_padding),
+ ColumnDimensions = new[]
{
- RelativeSizeAxes = Axes.Both,
- Children = new[]
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, column_padding),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, column_padding),
+ new Dimension(),
+ },
+ Content = new[]
+ {
+ new Drawable?[]
{
- new Container
+ new GridContainer
{
RelativeSizeAxes = Axes.Both,
- Masking = true,
- CornerRadius = 10,
- Child = new Box
+ RowDimensions = new[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary.
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
},
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding(20),
- Child = new Container
+ Content = new[]
{
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
- Child = new OsuContextMenuContainer
+ new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Child = new GridContainer
+ new OverlinedPlaylistHeader(room),
+ },
+ new Drawable[]
+ {
+ drawablePlaylist = new DrawableRoomPlaylist
{
RelativeSizeAxes = Axes.Both,
- ColumnDimensions = new[]
+ SelectedItem = { BindTarget = SelectedItem },
+ AllowSelection = true,
+ AllowShowingResults = true,
+ RequestResults = item =>
{
- new Dimension(),
- new Dimension(GridSizeMode.Absolute, 10),
- new Dimension(),
- new Dimension(GridSizeMode.Absolute, 10),
- new Dimension(),
- },
- Content = new[]
- {
- new Drawable?[]
- {
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = 5 },
- Content = new[]
- {
- new Drawable[] { new OverlinedPlaylistHeader(room), },
- new Drawable[]
- {
- drawablePlaylist = new DrawableRoomPlaylist
- {
- RelativeSizeAxes = Axes.Both,
- SelectedItem = { BindTarget = SelectedItem },
- AllowSelection = true,
- AllowShowingResults = true,
- RequestResults = item =>
- {
- Debug.Assert(room.RoomID != null);
- parentScreen?.Push(new PlaylistItemUserBestResultsScreen(room.RoomID.Value, item,
- api.LocalUser.Value.Id));
- }
- }
- },
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new Drawable[]
- {
- userModsSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Bottom = 10 },
- Alpha = 0,
- Children = new Drawable[]
- {
- new OverlinedHeader("Extra mods"),
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(10, 0),
- Children = new Drawable[]
- {
- new UserModSelectButton
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Width = 90,
- Height = 30,
- Text = "Select",
- Action = showUserModSelect,
- },
- new ModDisplay
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Current = userMods,
- Scale = new Vector2(0.8f),
- },
- }
- }
- }
- },
- },
- new Drawable[]
- {
- userStyleSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Margin = new MarginPadding { Bottom = 10 },
- Alpha = 0,
- Children = new Drawable[]
- {
- new OverlinedHeader("Difficulty"),
- userStyleDisplayContainer = new Container
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y
- }
- }
- },
- },
- new Drawable[]
- {
- progressSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- new OverlinedHeader("Progress"),
- new RoomLocalUserInfo(room),
- }
- },
- },
- new Drawable[]
- {
- new OverlinedHeader("Leaderboard")
- },
- new Drawable[] { leaderboard = new MatchLeaderboard(room) { RelativeSizeAxes = Axes.Both }, },
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new Drawable[] { new OverlinedHeader("Chat") },
- new Drawable[] { new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both } }
- },
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- }
- },
- },
- },
+ Debug.Assert(room.RoomID != null);
+ parentScreen?.Push(new PlaylistItemUserBestResultsScreen(room.RoomID.Value, item,
+ api.LocalUser.Value.Id));
+ }
}
}
}
},
- new Container
+ null,
+ new GridContainer
{
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.Both,
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, row_padding),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, row_padding),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, row_padding),
+ new Dimension(GridSizeMode.AutoSize),
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ userModsSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Extra mods"),
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new UserModSelectButton
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 90,
+ Height = 30,
+ Text = "Select",
+ Action = showUserModSelect,
+ },
+ new ModDisplay
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Current = userMods,
+ Scale = new Vector2(0.8f),
+ }
+ }
+ }
+ }
+ }
+ },
+ null,
+ new Drawable[]
+ {
+ userStyleSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Difficulty"),
+ userStyleDisplayContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y
+ }
+ }
+ }
+ },
+ null,
+ new Drawable[]
+ {
+ progressSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Progress"),
+ new RoomLocalUserInfo(room),
+ }
+ }
+ },
+ null,
+ new Drawable[]
+ {
+ new OverlinedHeader("Leaderboard")
+ },
+ new Drawable[]
+ {
+ leaderboard = new MatchLeaderboard(room)
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ }
+ }
},
+ null,
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize)
+ },
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new OverlinedHeader("Chat")
+ },
+ new Drawable[]
+ {
+ new MatchChatDisplay(room)
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ }
+ }
+ }
}
}
}
}
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- // Resolves 1px masking errors between the settings overlay and the room panel.
- Padding = new MarginPadding(-1),
- Child = settingsOverlay = new PlaylistsRoomSettingsOverlay(room)
- {
- EditPlaylist = () =>
- {
- if (this.IsCurrentScreen())
- this.Push(new PlaylistsSongSelect(room));
- },
- }
}
- },
- },
- },
- // Footer
- new Drawable[]
- {
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4Extensions.FromHex(@"28242d") // Temporary.
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding(5),
- Child = new PlaylistsRoomFooter(room)
- {
- OnStart = startPlay,
- OnClose = closePlaylist,
- }
- },
}
}
+ },
+ settingsOverlay = new PlaylistsRoomSettingsOverlay(room)
+ {
+ EditPlaylist = () =>
+ {
+ if (this.IsCurrentScreen())
+ this.Push(new PlaylistsSongSelect(room));
+ }
+ }
+ }
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = footer_height,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex(@"28242d") // Temporary.
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding(5),
+ Child = new PlaylistsRoomFooter(room)
+ {
+ OnStart = startPlay,
+ OnClose = closePlaylist
+ }
}
}
}
From f2c75ef593a816851c491fc5f2bdb51e83bcef60 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 14 Feb 2025 22:08:32 +0900
Subject: [PATCH 033/349] Always update selection when anything changes
If style changes, then we also need to re-validate mods. Thus, we should
just update the entire selection anyway, and merge the "gameplay state"
into it for simplicity.
---
.../Playlists/PlaylistsRoomSubScreen.cs | 77 ++++++++-----------
1 file changed, 32 insertions(+), 45 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 87bec5d8e1..38768097a4 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -444,17 +444,17 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
room.PropertyChanged += onRoomPropertyChanged;
isIdle.BindValueChanged(_ => updatePollingRate(), true);
- SelectedItem.BindValueChanged(_ => onSelectedItemChanged());
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
- beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateGameplayState());
+ beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateSelectionState());
- userBeatmap.BindValueChanged(_ => updateGameplayState());
- userRuleset.BindValueChanged(_ => updateGameplayState());
- userMods.BindValueChanged(_ => updateGameplayState());
+ SelectedItem.BindValueChanged(_ => updateSelectionState());
+ userBeatmap.BindValueChanged(_ => updateSelectionState());
+ userRuleset.BindValueChanged(_ => updateSelectionState());
+ userMods.BindValueChanged(_ => updateSelectionState());
updateSetupState();
- updateGameplayState();
+ updateSelectionState();
}
///
@@ -512,9 +512,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
///
- /// Responds to changes in the selected playlist item to validate the user's style selection.
+ /// Responds to changes in the selected playlist item or user style (beatmap/ruleset/mods) to validate and update global states in preparation for a gameplay session.
///
- private void onSelectedItemChanged()
+ private void updateSelectionState()
{
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
return;
@@ -530,28 +530,40 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (userBeatmap.Value != null && userBeatmap.Value.BeatmapSet!.OnlineID != item.Beatmap.BeatmapSet!.OnlineID)
userBeatmap.Value = null;
+ IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
+
// Reset ruleset style when no longer valid for the beatmap.
if (userRuleset.Value != null)
{
- IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
int beatmapRuleset = gameplayBeatmap.Ruleset.OnlineID;
-
if (beatmapRuleset > 0 && userRuleset.Value.OnlineID != beatmapRuleset)
userRuleset.Value = null;
}
RulesetInfo gameplayRuleset = userRuleset.Value ?? rulesets.GetRuleset(item.RulesetID)!;
Ruleset rulesetInstance = gameplayRuleset.CreateInstance();
+
+ // Remove any user mods that are no longer allowed.
Mod[] allowedMods = item.Freestyle
? rulesetInstance.AllMods.OfType().Where(m => ModUtils.IsValidFreeModForMatchType(m, room.Type)).ToArray()
: item.AllowedMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
-
- // Remove any user mods that are no longer allowed.
Mod[] newUserMods = userMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToArray();
if (!newUserMods.SequenceEqual(userMods.Value))
userMods.Value = newUserMods;
- if (allowedMods.Length > 0)
+ // Update global gameplay state to correspond to the new selection.
+ // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
+ int beatmapId = gameplayBeatmap.OnlineID;
+ var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == beatmapId);
+ Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
+ Ruleset.Value = gameplayRuleset;
+ Mods.Value = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToArray();
+
+ // Update UI elements to reflect the new selection.
+ bool freemods = allowedMods.Length > 0;
+ bool freestyle = item.Freestyle;
+
+ if (freemods)
{
userModsSection.Show();
userModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
@@ -563,37 +575,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
userModsSelectOverlay.IsValidMod = _ => false;
}
- if (item.Freestyle)
- userStyleSection.Show();
- else
- userStyleSection.Hide();
-
- updateGameplayState();
- }
-
- ///
- /// Adjusts the global beatmap/ruleset/mods values in preparation for a gameplay session.
- ///
- private void updateGameplayState()
- {
- if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
- return;
-
- IBeatmapInfo gameplayBeatmap = userBeatmap.Value ?? item.Beatmap;
- RulesetInfo gameplayRuleset = userRuleset.Value ?? rulesets.GetRuleset(item.RulesetID)!;
- Ruleset rulesetInstance = gameplayRuleset.CreateInstance();
- Mod[] gameplayMods = userMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToArray();
-
- // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
- int beatmapId = gameplayBeatmap.OnlineID;
- var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == beatmapId);
-
- Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
- Ruleset.Value = gameplayRuleset;
- Mods.Value = gameplayMods;
-
- if (item.Freestyle)
+ if (freestyle)
{
+ userStyleSection.Show();
+
PlaylistItem gameplayItem = item.With(ruleset: gameplayRuleset.OnlineID, beatmap: new Optional(gameplayBeatmap));
PlaylistItem? currentItem = userStyleDisplayContainer.SingleOrDefault()?.Item;
@@ -607,6 +592,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
};
}
}
+ else
+ userStyleSection.Hide();
}
///
@@ -688,8 +675,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
base.OnResuming(e);
beginHandlingTrack();
- // Required when resuming from style selection.
- updateGameplayState();
+ // Required to update beatmap/ruleset when resuming from style selection.
+ updateSelectionState();
}
public override bool OnExiting(ScreenExitEvent e)
From 06f27277bfe1730e450c188af5a82b7720a4a172 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Fri, 14 Feb 2025 22:54:03 +0900
Subject: [PATCH 034/349] Use margins to remove padding from hidden elements
If one of these elments is hidden, the following spacing element is
expected to be hidden too. Simplest is to use margins.
Old implementation already did this.
---
.../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index c0fe78134f..79728fc4b2 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -272,11 +272,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Absolute, row_padding),
new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Absolute, row_padding),
new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Absolute, row_padding),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
@@ -287,6 +284,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Bottom = row_padding },
Alpha = 0,
Children = new Drawable[]
{
@@ -319,13 +317,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
}
},
- null,
new Drawable[]
{
userStyleSection = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Bottom = row_padding },
Alpha = 0,
Children = new Drawable[]
{
@@ -338,13 +336,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
}
},
- null,
new Drawable[]
{
progressSection = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Bottom = row_padding },
Alpha = 0,
Direction = FillDirection.Vertical,
Children = new Drawable[]
@@ -354,7 +352,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
}
},
- null,
new Drawable[]
{
new OverlinedHeader("Leaderboard")
From 65cae7c7aafda7e764a3070958778f901d9f4c74 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Mon, 17 Feb 2025 15:02:38 +0900
Subject: [PATCH 035/349] Fix inverted condition
---
osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 79728fc4b2..4ab20f8bb0 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -764,7 +764,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (ExitConfirmed)
return true;
- if (api.State.Value == APIState.Online)
+ if (api.State.Value != APIState.Online)
return true;
bool hasUnsavedChanges = room.RoomID == null && room.Playlist.Count > 0;
From 9d7b01bcd47e789d86978508f0bf1148dc9ed066 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 20 Feb 2025 21:20:50 +0800
Subject: [PATCH 036/349] update guest difficulty display to consistent with
the web page
---
.../API/Requests/Responses/APIBeatmap.cs | 12 +++++
osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 50 +++++++++++++++++--
2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
index e5ecfe2c99..c6033e3255 100644
--- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs
@@ -103,6 +103,9 @@ namespace osu.Game.Online.API.Requests.Responses
public double BPM { get; set; }
+ [JsonProperty(@"owners")]
+ public BeatmapOwner[] BeatmapOwners { get; set; } = Array.Empty();
+
#region Implementation of IBeatmapInfo
public IBeatmapMetadataInfo Metadata => (BeatmapSet as IBeatmapSetInfo)?.Metadata ?? new BeatmapMetadata();
@@ -171,5 +174,14 @@ namespace osu.Game.Online.API.Requests.Responses
// ReSharper disable once NonReadonlyMemberInGetHashCode
public override int GetHashCode() => OnlineID;
}
+
+ public class BeatmapOwner
+ {
+ [JsonProperty(@"id")]
+ public int Id { get; set; }
+
+ [JsonProperty(@"username")]
+ public string Username { get; set; } = string.Empty;
+ }
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index a7838651a9..8e36b0ed32 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -211,19 +211,59 @@ namespace osu.Game.Overlays.BeatmapSet
private void showBeatmap(APIBeatmap? beatmapInfo)
{
guestMapperContainer.Clear();
+ var beatmapOwners = beatmapInfo?.BeatmapOwners;
- if (beatmapInfo?.AuthorID != BeatmapSet?.AuthorID)
+ if (beatmapOwners != null && (beatmapOwners.Length != 1 || beatmapOwners.First().Id != beatmapSet?.AuthorID))
{
- APIUser? user = BeatmapSet?.RelatedUsers?.SingleOrDefault(u => u.OnlineID == beatmapInfo?.AuthorID);
+ APIUser[]? users = BeatmapSet?.RelatedUsers?.Where(u => beatmapOwners.Any(o => o.Id == u.OnlineID)).ToArray();
- if (user != null)
+ if (users != null)
{
- guestMapperContainer.AddText("mapped by ");
- guestMapperContainer.AddUserLink(user);
+ formatGuestUser(users);
}
}
version.Text = beatmapInfo?.DifficultyName ?? string.Empty;
+ return;
+
+ void formatGuestUser(APIUser[] users)
+ {
+ int count = users.Length;
+
+ guestMapperContainer.AddText(BeatmapsetsStrings.ShowDetailsMappedBy(string.Empty)); // set string.Empty here because we need link.
+
+ switch (count)
+ {
+ case 1:
+ guestMapperContainer.AddUserLink(users[0]);
+ break;
+
+ case 2:
+ guestMapperContainer.AddUserLink(users[0]);
+ guestMapperContainer.AddText(CommonStrings.ArrayAndTwoWordsConnector);
+ guestMapperContainer.AddUserLink(users[1]);
+ break;
+
+ default:
+ {
+ for (int i = 0; i < count; i++)
+ {
+ guestMapperContainer.AddUserLink(users[i]);
+
+ if (i < count - 2)
+ {
+ guestMapperContainer.AddText(CommonStrings.ArrayAndWordsConnector);
+ }
+ else if (i == count - 2)
+ {
+ guestMapperContainer.AddText(CommonStrings.ArrayAndLastWordConnector);
+ }
+ }
+
+ break;
+ }
+ }
+ }
}
private void updateDifficultyButtons()
From cde50bca762d4646ecaec261a06af2bf62325971 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 20 Feb 2025 21:31:04 +0800
Subject: [PATCH 037/349] add test
---
.../Online/TestSceneBeatmapSetOverlay.cs | 57 +++++++++++++++----
1 file changed, 46 insertions(+), 11 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index 325cb9e0cb..ced95d09d9 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -297,6 +297,31 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("guest mapper information shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot"));
}
+ [Test]
+ public void TestBeatmapsetWithALotGuestOwner()
+ {
+ AddStep("show map with 2 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(2)));
+ AddStep("move mouse to guest difficulty", () =>
+ {
+ InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
+ });
+ AddStep("show map with 3 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(3)));
+ AddStep("move mouse to guest difficulty", () =>
+ {
+ InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
+ });
+ AddStep("show map with 10 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(20)));
+ AddStep("move mouse to guest difficulty", () =>
+ {
+ InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
+ });
+ AddStep("show map with 20 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(20)));
+ AddStep("move mouse to guest difficulty", () =>
+ {
+ InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
+ });
+ }
+
private APIBeatmapSet createManyDifficultiesBeatmapSet()
{
var set = getBeatmapSet();
@@ -336,22 +361,31 @@ namespace osu.Game.Tests.Visual.Online
return beatmapSet;
}
- private APIBeatmapSet createBeatmapSetWithGuestDifficulty()
+ private APIBeatmapSet createBeatmapSetWithGuestDifficulty(int guestCount = 1)
{
var set = getBeatmapSet();
var beatmaps = new List();
+ var beatmapOwners = new List();
+ var ownersAPIUser = new List();
- var guestUser = new APIUser
+ for (int i = 0; i < guestCount; i++)
{
- Username = @"BanchoBot",
- Id = 3,
- };
+ var guestUser = new APIUser
+ {
+ Username = @$"BanchoBot{i}",
+ Id = i + 3,
+ };
- set.RelatedUsers = new[]
- {
- set.Author, guestUser
- };
+ beatmapOwners.Add(new APIBeatmap.BeatmapOwner
+ {
+ Username = @$"BanchoBot{i}",
+ Id = i + 3,
+ });
+ ownersAPIUser.Add(guestUser);
+ }
+
+ set.RelatedUsers = new[] { set.Author }.Concat(ownersAPIUser).ToArray();
beatmaps.Add(new APIBeatmap
{
@@ -366,7 +400,7 @@ namespace osu.Game.Tests.Visual.Online
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
},
- Status = BeatmapOnlineStatus.Graveyard
+ Status = BeatmapOnlineStatus.Graveyard,
});
beatmaps.Add(new APIBeatmap
@@ -382,7 +416,8 @@ namespace osu.Game.Tests.Visual.Online
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
},
- Status = BeatmapOnlineStatus.Graveyard
+ Status = BeatmapOnlineStatus.Graveyard,
+ BeatmapOwners = beatmapOwners.ToArray(),
});
set.Beatmaps = beatmaps.ToArray();
From 4fa0288a20160299b9ecc5c43231f9f606a410a9 Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 20 Feb 2025 22:03:57 +0800
Subject: [PATCH 038/349] maybe not a function
---
osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 76 +++++++++----------
1 file changed, 35 insertions(+), 41 deletions(-)
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index 8e36b0ed32..ab3b8d882e 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -219,51 +219,45 @@ namespace osu.Game.Overlays.BeatmapSet
if (users != null)
{
- formatGuestUser(users);
+ int count = users.Length;
+
+ guestMapperContainer.AddText(BeatmapsetsStrings.ShowDetailsMappedBy(string.Empty)); // set string.Empty here because we need user link.
+
+ switch (count)
+ {
+ case 1:
+ guestMapperContainer.AddUserLink(users[0]);
+ break;
+
+ case 2:
+ guestMapperContainer.AddUserLink(users[0]);
+ guestMapperContainer.AddText(CommonStrings.ArrayAndTwoWordsConnector);
+ guestMapperContainer.AddUserLink(users[1]);
+ break;
+
+ default:
+ {
+ for (int i = 0; i < count; i++)
+ {
+ guestMapperContainer.AddUserLink(users[i]);
+
+ if (i < count - 2)
+ {
+ guestMapperContainer.AddText(CommonStrings.ArrayAndWordsConnector);
+ }
+ else if (i == count - 2)
+ {
+ guestMapperContainer.AddText(CommonStrings.ArrayAndLastWordConnector);
+ }
+ }
+
+ break;
+ }
+ }
}
}
version.Text = beatmapInfo?.DifficultyName ?? string.Empty;
- return;
-
- void formatGuestUser(APIUser[] users)
- {
- int count = users.Length;
-
- guestMapperContainer.AddText(BeatmapsetsStrings.ShowDetailsMappedBy(string.Empty)); // set string.Empty here because we need link.
-
- switch (count)
- {
- case 1:
- guestMapperContainer.AddUserLink(users[0]);
- break;
-
- case 2:
- guestMapperContainer.AddUserLink(users[0]);
- guestMapperContainer.AddText(CommonStrings.ArrayAndTwoWordsConnector);
- guestMapperContainer.AddUserLink(users[1]);
- break;
-
- default:
- {
- for (int i = 0; i < count; i++)
- {
- guestMapperContainer.AddUserLink(users[i]);
-
- if (i < count - 2)
- {
- guestMapperContainer.AddText(CommonStrings.ArrayAndWordsConnector);
- }
- else if (i == count - 2)
- {
- guestMapperContainer.AddText(CommonStrings.ArrayAndLastWordConnector);
- }
- }
-
- break;
- }
- }
- }
}
private void updateDifficultyButtons()
From 74d9acbede330ca26e1533d657fa5919390eb1eb Mon Sep 17 00:00:00 2001
From: cdwcgt
Date: Thu, 20 Feb 2025 22:45:14 +0800
Subject: [PATCH 039/349] fix test
---
osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index ced95d09d9..822e5f26bd 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -289,12 +289,12 @@ namespace osu.Game.Tests.Visual.Online
{
InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0));
});
- AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot"));
+ AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot0"));
AddStep("move mouse to guest difficulty", () =>
{
InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
});
- AddAssert("guest mapper information shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot"));
+ AddAssert("guest mapper information shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot0"));
}
[Test]
@@ -310,7 +310,7 @@ namespace osu.Game.Tests.Visual.Online
{
InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
});
- AddStep("show map with 10 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(20)));
+ AddStep("show map with 10 mapper", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty(10)));
AddStep("move mouse to guest difficulty", () =>
{
InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1));
From fa49b30b5cc077f807f60fd6964bf5416f5ec845 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Fri, 21 Feb 2025 11:30:52 +0100
Subject: [PATCH 040/349] Attempt to fix spectator list showing other users in
multiplayer room even if they're not spectating better
Maybe closes https://github.com/ppy/osu/issues/31972. Not sure. I have
no reproduction scenario to work with, no solid understanding of how
the issue can happen, and if this doesn't fix it, then I'm not even
entirely sure how this can ever be fixed client-side.
The working theory is that not watching updates to the room provoked a
situation wherein the room was temporarily not in a correct state when
`WatchingUsers` changed, therefore the collection change callback failed
to exclude other players in the room from display.
I'm only PRing this because of the `next-release` tag on the issue.
---
osu.Game/Screens/Play/HUD/SpectatorList.cs | 80 ++++++++++++++++------
1 file changed, 60 insertions(+), 20 deletions(-)
diff --git a/osu.Game/Screens/Play/HUD/SpectatorList.cs b/osu.Game/Screens/Play/HUD/SpectatorList.cs
index 4297c62712..98b3ede874 100644
--- a/osu.Game/Screens/Play/HUD/SpectatorList.cs
+++ b/osu.Game/Screens/Play/HUD/SpectatorList.cs
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@@ -38,8 +38,9 @@ namespace osu.Game.Screens.Play.HUD
public BindableColour4 HeaderColour { get; } = new BindableColour4(Colour4.White);
private BindableList watchingUsers { get; } = new BindableList();
+ private BindableList actualSpectators { get; } = new BindableList();
+
private Bindable userPlayingState { get; } = new Bindable();
- private int displayedSpectatorCount;
private OsuSpriteText header = null!;
private FillFlowContainer mainFlow = null!;
@@ -94,7 +95,9 @@ namespace osu.Game.Screens.Play.HUD
((IBindableList)watchingUsers).BindTo(client.WatchingUsers);
((IBindable)userPlayingState).BindTo(gameplayState.PlayingState);
- watchingUsers.BindCollectionChanged(onSpectatorsChanged, true);
+ watchingUsers.BindCollectionChanged(onWatchingUsersChanged, true);
+ multiplayerClient.RoomUpdated += removePlayersFromMultiplayerRoom;
+ actualSpectators.BindCollectionChanged(onSpectatorsChanged, true);
userPlayingState.BindValueChanged(_ => updateVisibility());
Font.BindValueChanged(_ => updateAppearance());
@@ -104,22 +107,55 @@ namespace osu.Game.Screens.Play.HUD
this.FadeInFromZero(200, Easing.OutQuint);
}
- private void onSpectatorsChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ private void onWatchingUsersChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ for (int i = 0; i < e.NewItems!.Count; i++)
+ actualSpectators.Add((SpectatorUser)e.NewItems![i]!);
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ for (int i = 0; i < e.OldItems!.Count; i++)
+ actualSpectators.Remove((SpectatorUser)e.OldItems![i]!);
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Reset:
+ {
+ actualSpectators.Clear();
+ break;
+ }
+
+ default:
+ throw new NotSupportedException();
+ }
+
+ removePlayersFromMultiplayerRoom();
+ }
+
+ private void removePlayersFromMultiplayerRoom()
+ {
+ if (multiplayerClient.Room == null)
+ return;
+
// the multiplayer gameplay leaderboard relies on calling `SpectatorClient.WatchUser()` to get updates on users' total scores.
// this has an unfortunate side effect of other players showing up in `SpectatorClient.WatchingUsers`.
//
// we do not generally wish to display other players in the room as spectators due to that implementation detail,
// therefore this code is intended to filter out those players on the client side.
- //
- // note that the way that this is done is rather specific to the multiplayer use case and therefore carries a lot of assumptions
- // (e.g. that the `MultiplayerRoomUser`s have the correct `State` at the point wherein they issue the `WatchUser()` calls).
- // the more proper way to do this (which is by subscribing to `WatchingUsers` and `RoomUpdated`, and doing a proper diff to a third list on any change of either)
- // is a lot more difficult to write correctly, given that we also rely on `BindableList`'s collection changed event arguments to properly animate this component.
- var excludedUserIds = new HashSet();
- if (multiplayerClient.Room != null)
- excludedUserIds.UnionWith(multiplayerClient.Room.Users.Where(u => u.State != MultiplayerUserState.Spectating).Select(u => u.UserID));
+ var excludedUserIds = multiplayerClient.Room.Users.Where(u => u.State != MultiplayerUserState.Spectating).Select(u => u.UserID).ToHashSet();
+ actualSpectators.RemoveAll(s => excludedUserIds.Contains(s.OnlineID));
+ }
+ private void onSpectatorsChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
@@ -129,9 +165,6 @@ namespace osu.Game.Screens.Play.HUD
var spectator = (SpectatorUser)e.NewItems![i]!;
int index = Math.Max(e.NewStartingIndex, 0) + i;
- if (excludedUserIds.Contains(spectator.OnlineID))
- continue;
-
if (index >= max_spectators_displayed)
break;
@@ -148,10 +181,10 @@ namespace osu.Game.Screens.Play.HUD
for (int i = 0; i < spectatorsFlow.Count; i++)
spectatorsFlow.SetLayoutPosition(spectatorsFlow[i], i);
- if (watchingUsers.Count >= max_spectators_displayed && spectatorsFlow.Count < max_spectators_displayed)
+ if (actualSpectators.Count >= max_spectators_displayed && spectatorsFlow.Count < max_spectators_displayed)
{
for (int i = spectatorsFlow.Count; i < max_spectators_displayed; i++)
- addNewSpectatorToList(i, watchingUsers[i]);
+ addNewSpectatorToList(i, actualSpectators[i]);
}
break;
@@ -167,8 +200,7 @@ namespace osu.Game.Screens.Play.HUD
throw new NotSupportedException();
}
- displayedSpectatorCount = watchingUsers.Count(s => !excludedUserIds.Contains(s.OnlineID));
- header.Text = SpectatorListStrings.SpectatorCount(displayedSpectatorCount).ToUpper();
+ header.Text = SpectatorListStrings.SpectatorCount(actualSpectators.Count).ToUpper();
updateVisibility();
for (int i = 0; i < spectatorsFlow.Count; i++)
@@ -193,7 +225,7 @@ namespace osu.Game.Screens.Play.HUD
private void updateVisibility()
{
// We don't want to show spectators when we are watching a replay.
- mainFlow.FadeTo(displayedSpectatorCount > 0 && userPlayingState.Value != LocalUserPlayingState.NotPlaying ? 1 : 0, 250, Easing.OutQuint);
+ mainFlow.FadeTo(actualSpectators.Count > 0 && userPlayingState.Value != LocalUserPlayingState.NotPlaying ? 1 : 0, 250, Easing.OutQuint);
}
private void updateAppearance()
@@ -204,6 +236,14 @@ namespace osu.Game.Screens.Play.HUD
Width = header.DrawWidth;
}
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (multiplayerClient.IsNotNull())
+ multiplayerClient.RoomUpdated -= removePlayersFromMultiplayerRoom;
+ }
+
private partial class SpectatorListEntry : PoolableDrawable
{
public Bindable Current { get; } = new Bindable();
From 8b2582a69d07adf343855b729dd143777abbcbf6 Mon Sep 17 00:00:00 2001
From: finadoggie <75299710+Finadoggie@users.noreply.github.com>
Date: Sat, 22 Feb 2025 16:54:27 -0800
Subject: [PATCH 041/349] Add tip pressure threshold slider ingame
---
.../Settings/Sections/Input/TabletSettings.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
index 00ffbc1120..2cce6f18ec 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -45,6 +45,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private readonly BindableNumber rotation = new BindableNumber { MinValue = 0, MaxValue = 360 };
+ private readonly BindableNumber pressureThreshold = new BindableNumber { MinValue = 0, MaxValue = 100 };
+
[Resolved]
private GameHost host { get; set; }
@@ -213,6 +215,13 @@ namespace osu.Game.Overlays.Settings.Sections.Input
Current = sizeY,
CanBeShown = { BindTarget = enabled }
},
+ new SettingsSlider
+ {
+ TransferValueOnCommit = true,
+ LabelText = "Tip Threshold",
+ Current = pressureThreshold,
+ CanBeShown = { BindTarget = enabled }
+ },
}
},
};
@@ -267,6 +276,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
aspectRatioApplication = Schedule(() => forceAspectRatio(aspect.NewValue));
});
+ pressureThreshold.BindTo(tabletHandler.PressureThreshold);
+
tablet.BindTo(tabletHandler.Tablet);
tablet.BindValueChanged(val => Schedule(() =>
{
From 543ad5b2a47591652d04ac66eb8730cafd7e06b9 Mon Sep 17 00:00:00 2001
From: Kunologist <2014709936@qq.com>
Date: Mon, 24 Feb 2025 14:16:33 +0800
Subject: [PATCH 042/349] Add alt+wheel volume adjustment on result screen
---
osu.Game/Screens/Ranking/ResultsScreen.cs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs
index fe0d805cee..8fb3c66054 100644
--- a/osu.Game/Screens/Ranking/ResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/ResultsScreen.cs
@@ -26,6 +26,7 @@ using osu.Game.Localisation;
using osu.Game.Online.API;
using osu.Game.Online.Placeholders;
using osu.Game.Overlays;
+using osu.Game.Overlays.Volume;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
@@ -122,6 +123,7 @@ namespace osu.Game.Screens.Ranking
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
+ new GlobalScrollAdjustsVolume(),
StatisticsPanel = createStatisticsPanel().With(panel =>
{
panel.RelativeSizeAxes = Axes.Both;
@@ -503,12 +505,24 @@ namespace osu.Game.Screens.Ranking
{
}
+ protected override bool OnScroll(ScrollEvent e)
+ {
+ // Match stable behaviour of only alt-scroll adjusting volume.
+ // This is the same behaviour as the song selection screen.
+ if (!e.CurrentState.Keyboard.AltPressed)
+ return true;
+
+ return base.OnScroll(e);
+ }
+
protected partial class VerticalScrollContainer : OsuScrollContainer
{
protected override Container Content => content;
private readonly Container content;
+ protected override bool OnScroll(ScrollEvent e) => !e.ControlPressed && !e.AltPressed && !e.ShiftPressed && !e.SuperPressed;
+
public VerticalScrollContainer()
{
Masking = false;
From fc2d8bfe5f3b4ed3d1a0f7652dd84601e8115b75 Mon Sep 17 00:00:00 2001
From: finadoggie <75299710+Finadoggie@users.noreply.github.com>
Date: Tue, 25 Feb 2025 00:25:51 -0800
Subject: [PATCH 043/349] Clamp slider from 0 to 1
---
osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
index 2cce6f18ec..9d70e49659 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private readonly BindableNumber rotation = new BindableNumber { MinValue = 0, MaxValue = 360 };
- private readonly BindableNumber pressureThreshold = new BindableNumber { MinValue = 0, MaxValue = 100 };
+ private readonly BindableNumber pressureThreshold = new BindableNumber { MinValue = 0.0f, MaxValue = 1.0f, Precision = 0.005f };
[Resolved]
private GameHost host { get; set; }
From 13ca8c20f6fa71bd196e30a5987cb112cbc7214f Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 25 Feb 2025 21:54:13 +0900
Subject: [PATCH 044/349] Make results screens use tasks to fetch scores
---
.../Visual/Ranking/TestSceneResultsScreen.cs | 17 +--
.../Spectate/MultiSpectatorResultsScreen.cs | 7 +-
.../Playlists/PlaylistItemResultsScreen.cs | 112 ++++++++++--------
.../PlaylistItemScoreResultsScreen.cs | 5 +-
.../PlaylistItemUserBestResultsScreen.cs | 5 +-
osu.Game/Screens/Ranking/ResultsScreen.cs | 51 +++-----
osu.Game/Screens/Ranking/SoloResultsScreen.cs | 31 +++--
7 files changed, 117 insertions(+), 111 deletions(-)
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
index 3a08756090..4acbdb4a76 100644
--- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs
@@ -17,7 +17,6 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Osu;
@@ -416,7 +415,7 @@ namespace osu.Game.Tests.Visual.Ranking
RetryOverlay = InternalChildren.OfType().SingleOrDefault();
}
- protected override APIRequest FetchScores(Action> scoresCallback)
+ protected override Task> FetchScores()
{
var scores = new List();
@@ -428,9 +427,7 @@ namespace osu.Game.Tests.Visual.Ranking
scores.Add(score);
}
- scoresCallback.Invoke(scores);
-
- return null;
+ return Task.FromResult>(scores);
}
}
@@ -446,9 +443,9 @@ namespace osu.Game.Tests.Visual.Ranking
this.fetchWaitTask = fetchWaitTask ?? Task.CompletedTask;
}
- protected override APIRequest FetchScores(Action> scoresCallback)
+ protected override Task> FetchScores()
{
- Task.Run(async () =>
+ return Task.Run>(async () =>
{
await fetchWaitTask;
@@ -461,12 +458,10 @@ namespace osu.Game.Tests.Visual.Ranking
scores.Add(score);
}
- scoresCallback?.Invoke(scores);
-
Schedule(() => FetchCompleted = true);
- });
- return null;
+ return scores;
+ });
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs
index c240bbea0c..6e2f90e3b5 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs
@@ -1,9 +1,8 @@
// 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;
+using System.Threading.Tasks;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
@@ -23,8 +22,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
Scheduler.AddDelayed(() => StatisticsPanel.ToggleVisibility(), 1000);
}
- protected override APIRequest? FetchScores(Action> scoresCallback) => null;
+ protected override Task> FetchScores() => Task.FromResult>([]);
- protected override APIRequest? FetchNextPage(int direction, Action> scoresCallback) => null;
+ protected override Task> FetchNextPage(int direction) => Task.FromResult>([]);
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
index 13ef5d6f64..ed90b3b1ae 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -76,16 +77,21 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected abstract APIRequest CreateScoreRequest();
- protected sealed override APIRequest FetchScores(Action> scoresCallback)
+ protected override async Task> FetchScores()
{
// This performs two requests:
// 1. A request to show the relevant score (and scores around).
// 2. If that fails, a request to index the room starting from the highest score.
+ var requestTaskSource = new TaskCompletionSource();
var userScoreReq = CreateScoreRequest();
+ userScoreReq.Success += requestTaskSource.SetResult;
+ userScoreReq.Failure += requestTaskSource.SetException;
+ API.Queue(userScoreReq);
- userScoreReq.Success += userScore =>
+ try
{
+ var userScore = await requestTaskSource.Task;
var allScores = new List { userScore };
// Other scores could have arrived between score submission and entering the results screen. Ensure the local player score position is up to date.
@@ -113,88 +119,96 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
setPositions(lowerScores, userScore.Position.Value, 1);
}
- Schedule(() =>
- {
- PerformSuccessCallback(scoresCallback, allScores);
- hideLoadingSpinners();
- });
- };
-
- // On failure, fallback to a normal index.
- userScoreReq.Failure += _ => API.Queue(createIndexRequest(scoresCallback));
-
- return userScoreReq;
+ return TransformScores(allScores);
+ }
+ catch (OperationCanceledException)
+ {
+ return [];
+ }
+ catch
+ {
+ return await fetchScoresAround();
+ }
+ finally
+ {
+ Schedule(() => hideLoadingSpinners());
+ }
}
- protected override APIRequest? FetchNextPage(int direction, Action> scoresCallback)
+ protected override async Task> FetchNextPage(int direction)
{
Debug.Assert(direction == 1 || direction == -1);
MultiplayerScores? pivot = direction == -1 ? higherScores : lowerScores;
-
if (pivot?.Cursor == null)
- return null;
+ return [];
- if (pivot == higherScores)
- LeftSpinner.Show();
- else
- RightSpinner.Show();
+ Schedule(() =>
+ {
+ if (pivot == higherScores)
+ LeftSpinner.Show();
+ else
+ RightSpinner.Show();
+ });
- return createIndexRequest(scoresCallback, pivot);
+ return await fetchScoresAround(pivot);
}
///
/// Creates a with an optional score pivot.
///
/// Does not queue the request.
- /// The callback to perform with the resulting scores.
/// An optional score pivot to retrieve scores around. Can be null to retrieve scores from the highest score.
- /// The indexing .
- private APIRequest createIndexRequest(Action> scoresCallback, MultiplayerScores? pivot = null)
+ private async Task> fetchScoresAround(MultiplayerScores? pivot = null)
{
+ var requestTaskSource = new TaskCompletionSource();
var indexReq = pivot != null
? new IndexPlaylistScoresRequest(RoomId, PlaylistItem.ID, pivot.Cursor, pivot.Params)
: new IndexPlaylistScoresRequest(RoomId, PlaylistItem.ID);
+ indexReq.Success += requestTaskSource.SetResult;
+ indexReq.Failure += requestTaskSource.SetException;
+ API.Queue(indexReq);
- indexReq.Success += r =>
+ try
{
+ var index = await requestTaskSource.Task;
+
if (pivot == lowerScores)
{
- lowerScores = r;
- setPositions(r, pivot, 1);
+ lowerScores = index;
+ setPositions(index, pivot, 1);
}
else
{
- higherScores = r;
- setPositions(r, pivot, -1);
+ higherScores = index;
+ setPositions(index, pivot, -1);
}
- Schedule(() =>
- {
- PerformSuccessCallback(scoresCallback, r.Scores, r);
- hideLoadingSpinners(r);
- });
- };
-
- indexReq.Failure += _ => hideLoadingSpinners(pivot);
-
- return indexReq;
+ return TransformScores(index.Scores, index);
+ }
+ catch (OperationCanceledException)
+ {
+ return [];
+ }
+ finally
+ {
+ Schedule(() => hideLoadingSpinners(pivot));
+ }
}
///
/// Transforms returned into s, ensure the is put into a sane state, and invokes a given success callback.
///
- /// The callback to invoke with the final s.
/// The s that were retrieved from s.
/// An optional pivot around which the scores were retrieved.
- protected virtual ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null)
+ protected virtual ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
{
- var scoreInfos = scores.Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, Beatmap.Value.BeatmapInfo)).OrderByTotalScore().ToArray();
-
- // Invoke callback to add the scores. Exclude the score provided to this screen since it's added already.
- callback.Invoke(scoreInfos.Where(s => s.OnlineID != Score?.OnlineID));
-
- return scoreInfos;
+ // Exclude the score provided to this screen since it's added already.
+ return scores
+ .Where(s => s.ID != Score?.OnlineID)
+ .Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, Beatmap.Value.BeatmapInfo))
+ .OrderByTotalScore()
+ .ToArray();
}
private void hideLoadingSpinners(MultiplayerScores? pivot = null)
@@ -213,7 +227,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
/// The to set positions on.
/// The pivot.
/// The amount to increment the pivot position by for each in .
- private void setPositions(MultiplayerScores scores, MultiplayerScores? pivot, int increment)
+ private static void setPositions(MultiplayerScores scores, MultiplayerScores? pivot, int increment)
=> setPositions(scores, pivot?.Scores[^1].Position ?? 0, increment);
///
@@ -222,7 +236,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
/// The to set positions on.
/// The pivot position.
/// The amount to increment the pivot position by for each in .
- private void setPositions(MultiplayerScores scores, int pivotPosition, int increment)
+ private static void setPositions(MultiplayerScores scores, int pivotPosition, int increment)
{
foreach (var s in scores.Scores)
{
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
index 05c03a4b28..c6c10e4d91 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.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 System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Online.API;
@@ -31,9 +30,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override APIRequest CreateScoreRequest() => new ShowPlaylistScoreRequest(RoomId, PlaylistItem.ID, scoreId);
- protected override ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null)
+ protected override ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
{
- var scoreInfos = base.PerformSuccessCallback(callback, scores, pivot);
+ var scoreInfos = base.TransformScores(scores, pivot);
Schedule(() => SelectedScore.Value ??= scoreInfos.SingleOrDefault(s => s.OnlineID == scoreId));
return scoreInfos;
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs
index 5b20496dba..1a0df0291c 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.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 System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Online.API;
@@ -25,9 +24,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override APIRequest CreateScoreRequest() => new ShowPlaylistUserScoreRequest(RoomId, PlaylistItem.ID, userId);
- protected override ScoreInfo[] PerformSuccessCallback(Action> callback, List scores, MultiplayerScores? pivot = null)
+ protected override ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
{
- var scoreInfos = base.PerformSuccessCallback(callback, scores, pivot);
+ var scoreInfos = base.TransformScores(scores, pivot);
Schedule(() =>
{
diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs
index fe0d805cee..11e90a06b9 100644
--- a/osu.Game/Screens/Ranking/ResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/ResultsScreen.cs
@@ -5,10 +5,12 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -23,7 +25,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Localisation;
-using osu.Game.Online.API;
using osu.Game.Online.Placeholders;
using osu.Game.Overlays;
using osu.Game.Scoring;
@@ -60,9 +61,6 @@ namespace osu.Game.Screens.Ranking
private bool skipExitTransition;
- [Resolved]
- private IAPIProvider api { get; set; } = null!;
-
protected StatisticsPanel StatisticsPanel { get; private set; } = null!;
private Drawable bottomPanel = null!;
@@ -237,10 +235,7 @@ namespace osu.Game.Screens.Ranking
{
base.LoadComplete();
- var req = FetchScores(fetchScoresCallback);
-
- if (req != null)
- api.Queue(req);
+ FetchScores().ContinueWith(t => addScores(t.GetResultSafely()));
StatisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true);
}
@@ -251,18 +246,16 @@ namespace osu.Game.Screens.Ranking
if (lastFetchCompleted)
{
- APIRequest? nextPageRequest = null;
+ Task> nextPageTask = Task.FromResult>([]);
if (ScorePanelList.IsScrolledToStart)
- nextPageRequest = FetchNextPage(-1, fetchScoresCallback);
+ nextPageTask = FetchNextPage(-1);
else if (ScorePanelList.IsScrolledToEnd)
- nextPageRequest = FetchNextPage(1, fetchScoresCallback);
+ nextPageTask = FetchNextPage(1);
- if (nextPageRequest != null)
- {
- lastFetchCompleted = false;
- api.Queue(nextPageRequest);
- }
+ nextPageTask.ContinueWith(t => addScores(t.GetResultSafely()), TaskContinuationOptions.OnlyOnRanToCompletion);
+
+ lastFetchCompleted = nextPageTask.IsCompletedSuccessfully;
}
}
@@ -329,17 +322,13 @@ namespace osu.Game.Screens.Ranking
///
/// Performs a fetch/refresh of scores to be displayed.
///
- /// A callback which should be called when fetching is completed. Scheduling is not required.
- /// An responsible for the fetch operation. This will be queued and performed automatically.
- protected virtual APIRequest? FetchScores(Action> scoresCallback) => null;
+ protected virtual Task> FetchScores() => Task.FromResult>([]);
///
- /// Performs a fetch of the next page of scores. This is invoked every frame until a non-null is returned.
+ /// Performs a fetch of the next page of scores. This is invoked every frame.
///
/// The fetch direction. -1 to fetch scores greater than the current start of the list, and 1 to fetch scores lower than the current end of the list.
- /// A callback which should be called when fetching is completed. Scheduling is not required.
- /// An responsible for the fetch operation. This will be queued and performed automatically.
- protected virtual APIRequest? FetchNextPage(int direction, Action> scoresCallback) => null;
+ protected virtual Task> FetchNextPage(int direction) => Task.FromResult>([]);
///
/// Creates the to be used to display extended information about scores.
@@ -351,10 +340,14 @@ namespace osu.Game.Screens.Ranking
: new StatisticsPanel();
}
- private void fetchScoresCallback(IEnumerable scores) => Schedule(() =>
+ private void addScores(IEnumerable scores) => Schedule(() =>
{
foreach (var s in scores)
- addScore(s);
+ {
+ var panel = ScorePanelList.AddScore(s);
+ if (detachedPanel != null)
+ panel.Alpha = 0;
+ }
// allow a frame for scroll container to adjust its dimensions with the added scores before fetching again.
Schedule(() => lastFetchCompleted = true);
@@ -409,14 +402,6 @@ namespace osu.Game.Screens.Ranking
return false;
}
- private void addScore(ScoreInfo score)
- {
- var panel = ScorePanelList.AddScore(score);
-
- if (detachedPanel != null)
- panel.Alpha = 0;
- }
-
private ScorePanel? detachedPanel;
private void onStatisticsStateChanged(ValueChangedEvent state)
diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
index 9f7604aa82..0593d5f91f 100644
--- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs
@@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@@ -21,26 +23,36 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
+ [Resolved]
+ private IAPIProvider api { get; set; } = null!;
+
public SoloResultsScreen(ScoreInfo score)
: base(score)
{
}
- protected override APIRequest? FetchScores(Action> scoresCallback)
+ protected override async Task> FetchScores()
{
Debug.Assert(Score != null);
if (Score.BeatmapInfo!.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
- return null;
+ return [];
+
+ var requestTaskSource = new TaskCompletionSource();
getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset);
- getScoreRequest.Success += r =>
+ getScoreRequest.Success += requestTaskSource.SetResult;
+ getScoreRequest.Failure += requestTaskSource.SetException;
+ api.Queue(getScoreRequest);
+
+ try
{
+ var scores = await requestTaskSource.Task;
var toDisplay = new List();
- for (int i = 0; i < r.Scores.Count; ++i)
+ for (int i = 0; i < scores.Scores.Count; ++i)
{
- var score = r.Scores[i];
+ var score = scores.Scores[i];
int position = i + 1;
if (score.MatchesOnlineID(Score))
@@ -58,9 +70,12 @@ namespace osu.Game.Screens.Ranking
}
}
- scoresCallback.Invoke(toDisplay);
- };
- return getScoreRequest;
+ return toDisplay;
+ }
+ catch (OperationCanceledException)
+ {
+ return [];
+ }
}
protected override void Dispose(bool isDisposing)
From dfae11101f8b968611a442691b794066a52538c7 Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 25 Feb 2025 22:37:12 +0900
Subject: [PATCH 045/349] Populate playlists results screen with online
beatmaps
---
osu.Game/Online/Rooms/MultiplayerScore.cs | 3 +++
.../Playlists/PlaylistItemResultsScreen.cs | 26 ++++++++++++++++---
.../PlaylistItemScoreResultsScreen.cs | 5 ++--
.../PlaylistItemUserBestResultsScreen.cs | 5 ++--
4 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/osu.Game/Online/Rooms/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs
index 2adee26da3..74eaea8dbc 100644
--- a/osu.Game/Online/Rooms/MultiplayerScore.cs
+++ b/osu.Game/Online/Rooms/MultiplayerScore.cs
@@ -80,6 +80,9 @@ namespace osu.Game.Online.Rooms
[JsonProperty("ruleset_id")]
public int RulesetId { get; set; }
+ [JsonProperty("beatmap_id")]
+ public int BeatmapId { get; set; }
+
public ScoreInfo CreateScoreInfo(ScoreManager scoreManager, RulesetStore rulesets, [NotNull] BeatmapInfo beatmap)
{
var ruleset = rulesets.GetRuleset(RulesetId);
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
index ed90b3b1ae..bba30ec312 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
@@ -9,8 +9,11 @@ using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
+using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@@ -39,6 +42,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
[Resolved]
protected RulesetStore Rulesets { get; private set; } = null!;
+ [Resolved]
+ private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
+
protected PlaylistItemResultsScreen(ScoreInfo? score, long roomId, PlaylistItem playlistItem)
: base(score)
{
@@ -119,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
setPositions(lowerScores, userScore.Position.Value, 1);
}
- return TransformScores(allScores);
+ return await TransformScores(allScores);
}
catch (OperationCanceledException)
{
@@ -184,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
setPositions(index, pivot, -1);
}
- return TransformScores(index.Scores, index);
+ return await TransformScores(index.Scores, index);
}
catch (OperationCanceledException)
{
@@ -201,12 +207,24 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
///
/// The s that were retrieved from s.
/// An optional pivot around which the scores were retrieved.
- protected virtual ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
+ protected virtual async Task TransformScores(List scores, MultiplayerScores? pivot = null)
{
+ APIBeatmap?[] beatmaps = await beatmapLookupCache.GetBeatmapsAsync(scores.Select(s => s.BeatmapId).Distinct().ToArray());
+
+ // Minimal data required to get various components in this screen to display correctly.
+ Dictionary beatmapsById = beatmaps.Where(b => b != null).ToDictionary(b => b!.OnlineID, b => new BeatmapInfo
+ {
+ Difficulty = new BeatmapDifficulty(b!.Difficulty),
+ DifficultyName = b.DifficultyName,
+ StarRating = b.StarRating,
+ Length = b.Length,
+ BPM = b.BPM
+ });
+
// Exclude the score provided to this screen since it's added already.
return scores
.Where(s => s.ID != Score?.OnlineID)
- .Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, Beatmap.Value.BeatmapInfo))
+ .Select(s => s.CreateScoreInfo(ScoreManager, Rulesets, beatmapsById.GetValueOrDefault(s.BeatmapId) ?? Beatmap.Value.BeatmapInfo))
.OrderByTotalScore()
.ToArray();
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
index c6c10e4d91..f74b30c3f7 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Scoring;
@@ -30,9 +31,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override APIRequest CreateScoreRequest() => new ShowPlaylistScoreRequest(RoomId, PlaylistItem.ID, scoreId);
- protected override ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
+ protected override async Task TransformScores(List scores, MultiplayerScores? pivot = null)
{
- var scoreInfos = base.TransformScores(scores, pivot);
+ var scoreInfos = await base.TransformScores(scores, pivot);
Schedule(() => SelectedScore.Value ??= scoreInfos.SingleOrDefault(s => s.OnlineID == scoreId));
return scoreInfos;
}
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs
index 1a0df0291c..2e763666a7 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemUserBestResultsScreen.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Scoring;
@@ -24,9 +25,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override APIRequest CreateScoreRequest() => new ShowPlaylistUserScoreRequest(RoomId, PlaylistItem.ID, userId);
- protected override ScoreInfo[] TransformScores(List scores, MultiplayerScores? pivot = null)
+ protected override async Task TransformScores(List scores, MultiplayerScores? pivot = null)
{
- var scoreInfos = base.TransformScores(scores, pivot);
+ var scoreInfos = await base.TransformScores(scores, pivot);
Schedule(() =>
{
From 8a27b6689edf50cace897a3009640ff1ba8b2e7e Mon Sep 17 00:00:00 2001
From: Dan Balasescu
Date: Tue, 25 Feb 2025 22:51:36 +0900
Subject: [PATCH 046/349] Replace virtual async method with better abstraction
---
.../Playlists/PlaylistItemResultsScreen.cs | 9 ++++-----
.../Playlists/PlaylistItemScoreResultsScreen.cs | 8 +++-----
.../Playlists/PlaylistItemUserBestResultsScreen.cs | 14 ++++----------
osu.Game/Screens/Ranking/ResultsScreen.cs | 10 ++++++++++
4 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
index bba30ec312..e9ba3bdb70 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemResultsScreen.cs
@@ -125,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
setPositions(lowerScores, userScore.Position.Value, 1);
}
- return await TransformScores(allScores);
+ return await transformScores(allScores);
}
catch (OperationCanceledException)
{
@@ -190,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
setPositions(index, pivot, -1);
}
- return await TransformScores(index.Scores, index);
+ return await transformScores(index.Scores);
}
catch (OperationCanceledException)
{
@@ -203,11 +203,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
///
- /// Transforms returned into s, ensure the is put into a sane state, and invokes a given success callback.
+ /// Transforms returned into s.
///
/// The s that were retrieved from s.
- /// An optional pivot around which the scores were retrieved.
- protected virtual async Task TransformScores(List scores, MultiplayerScores? pivot = null)
+ private async Task transformScores(List scores)
{
APIBeatmap?[] beatmaps = await beatmapLookupCache.GetBeatmapsAsync(scores.Select(s => s.BeatmapId).Distinct().ToArray());
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
index f74b30c3f7..7f386cd293 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistItemScoreResultsScreen.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Scoring;
@@ -31,11 +30,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override APIRequest CreateScoreRequest() => new ShowPlaylistScoreRequest(RoomId, PlaylistItem.ID, scoreId);
- protected override async Task TransformScores(List scores, MultiplayerScores? pivot = null)
+ protected override void OnScoresAdded(IEnumerable