diff --git a/osu.Android.props b/osu.Android.props
index 0ebb6be7a1..4699beeac0 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -10,7 +10,7 @@
true
-
+
System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
- * --TearDown
- * at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
- * at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
- * at osu.Framework.Extensions.TaskExtensions.WaitSafely(Task task)
- * at osu.Framework.Testing.TestScene.checkForErrors()
- * at osu.Framework.Testing.TestScene.RunTestsFromNUnit()
- *--ArgumentOutOfRangeException
- * at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
- * at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
- * at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
- * at osu.Game.Online.Multiplayer.MultiplayerClient.<>c__DisplayClass106_0.b__0() in C:\BuildAgent\work\ecd860037212ac52\osu.Game\Online\Multiplayer\MultiplayerClient .cs:line 702
- * at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
- */
public void TestCreatedRoom()
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -112,16 +96,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestTaikoOnlyMod()
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
- AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -133,32 +119,36 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestSettingValidity()
{
AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value);
AddStep("set playlist", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value);
}
[Test]
- [FlakyTest] // See above
public void TestStartMatchWhileSpectating()
{
AddStep("set playlist", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -179,16 +169,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestFreeModSelectionHasAllowedMods()
{
AddStep("add playlist item with allowed mod", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
- AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -206,16 +198,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestModSelectKeyWithAllowedMods()
{
AddStep("add playlist item with allowed mod", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
- AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -228,15 +222,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestModSelectKeyWithNoAllowedMods()
{
AddStep("add playlist item with no allowed mods", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
- });
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ }
+ ];
});
ClickButtonWhenEnabled();
@@ -249,13 +245,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestNextPlaylistItemSelectedAfterCompletion()
{
AddStep("add two playlist items", () =>
{
- SelectedRoom.Value.Playlist.AddRange(new[]
- {
+ SelectedRoom.Value.Playlist =
+ [
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
@@ -264,7 +259,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
- });
+ ];
});
ClickButtonWhenEnabled();
@@ -286,24 +281,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
- [FlakyTest] // See above
public void TestModSelectOverlay()
{
AddStep("add playlist item", () =>
{
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
- RequiredMods = new[]
+ SelectedRoom.Value.Playlist =
+ [
+ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
- new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }),
- new APIMod(new OsuModStrictTracking()),
- },
- AllowedMods = new[]
- {
- new APIMod(new OsuModFlashlight()),
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
+ RequiredMods = new[]
+ {
+ new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }),
+ new APIMod(new OsuModStrictTracking()),
+ },
+ AllowedMods = new[]
+ {
+ new APIMod(new OsuModFlashlight()),
+ }
}
- });
+ ];
});
ClickButtonWhenEnabled();
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
index aaf85dab7c..94dd114c32 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -22,7 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerPlayer : MultiplayerTestScene
{
- private MultiplayerPlayer player;
+ private MultiplayerPlayer player = null!;
[Test]
public void TestGameplay()
@@ -49,7 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("score changed", () => player.GameplayState.ScoreProcessor.TotalScore.Value > 0);
}
- private void setup(Func> mods = null)
+ private void setup(Func>? mods = null)
{
AddStep("set beatmap", () =>
{
@@ -64,10 +62,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("initialise gameplay", () =>
{
- Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo)
+ Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom!, new PlaylistItem(Beatmap.Value.BeatmapInfo)
{
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
- }, MultiplayerClient.ServerRoom?.Users.ToArray()));
+ }, MultiplayerClient.ServerRoom!.Users.ToArray()));
});
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
index 3baabecd84..518ea2b511 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs
@@ -28,9 +28,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerPlaylist : MultiplayerTestScene
{
- [Cached(typeof(IBindable))]
- private readonly Bindable currentItem = new Bindable();
-
private MultiplayerPlaylist list = null!;
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
@@ -51,12 +48,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create list", () =>
{
- Child = list = new MultiplayerPlaylist
+ Child = list = new MultiplayerPlaylist(SelectedRoom.Value)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
- Size = new Vector2(0.4f, 0.8f)
+ Size = new Vector2(0.4f, 0.8f),
+ SelectedItem = new Bindable()
};
});
@@ -166,9 +164,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RoomManager.CreateRoom(new Room
{
- Name = { Value = "test name" },
+ Name = "test name",
Playlist =
- {
+ [
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
RulesetID = Ruleset.Value.OnlineID
@@ -178,7 +176,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
RulesetID = Ruleset.Value.OnlineID,
Expired = true
}
- }
+ ]
});
});
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
index 47fb4e06ea..50f55ebde0 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Linq;
using NUnit.Framework;
@@ -27,10 +25,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerQueueList : MultiplayerTestScene
{
- private MultiplayerQueueList playlist;
- private BeatmapManager beatmaps;
- private BeatmapSetInfo importedSet;
- private BeatmapInfo importedBeatmap;
+ private MultiplayerQueueList playlist = null!;
+ private BeatmapManager beatmaps = null!;
+ private BeatmapSetInfo importedSet = null!;
+ private BeatmapInfo importedBeatmap = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create playlist", () =>
{
- Child = playlist = new MultiplayerQueueList
+ Child = playlist = new MultiplayerQueueList(SelectedRoom.Value)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 300),
- Items = { BindTarget = MultiplayerClient.ClientAPIRoom!.Playlist }
+ };
+
+ MultiplayerClient.ClientAPIRoom!.PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ playlist.Items.ReplaceRange(0, playlist.Items.Count, MultiplayerClient.ClientAPIRoom.Playlist);
};
});
@@ -69,7 +72,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestDeleteButtonAlwaysVisibleForHost()
{
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
- AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
assertDeleteButtonVisibility(1, true);
@@ -81,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestDeleteButtonOnlyVisibleForItemOwnerIfNotHost()
{
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
- AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
AddStep("join other user", () => MultiplayerClient.AddUser(new APIUser { Id = 1234 }));
AddStep("set other user as host", () => MultiplayerClient.TransferHost(1234));
@@ -100,7 +103,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestSingleItemDoesNotHaveDeleteButton()
{
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
- AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
assertDeleteButtonVisibility(0, false);
}
@@ -109,7 +112,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestCurrentItemHasDeleteButtonIfNotSingle()
{
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
- AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
+ AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
index 5ae5d1e228..b4f55bb6a3 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
@@ -26,9 +26,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerSpectateButton : MultiplayerTestScene
{
- [Cached(typeof(IBindable))]
- private readonly Bindable currentItem = new Bindable();
-
private MultiplayerSpectateButton spectateButton = null!;
private MatchStartControl startControl = null!;
@@ -51,13 +48,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create button", () =>
{
- AvailabilityTracker.SelectedItem.BindTo(currentItem);
+ PlaylistItem item = SelectedRoom.Value.Playlist.First();
+
+ AvailabilityTracker.SelectedItem.Value = item;
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
- currentItem.Value = SelectedRoom.Value.Playlist.First();
-
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
@@ -72,12 +69,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200, 50),
+ SelectedItem = new Bindable(item)
},
startControl = new MatchStartControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200, 50),
+ SelectedItem = new Bindable(item)
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index cc78bed5de..b55102518a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
@@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestItemAddedWhenCreateNewItemClicked()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
}
[Test]
public void TestItemNotAddedIfExistingOnStart()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("finalise selection", () => songSelect.FinaliseSelection());
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
}
@@ -89,24 +89,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestAddSameItemMultipleTimes()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2);
}
[Test]
public void TestAddItemAfterRearrangement()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddStep("rearrange", () =>
- {
- var item = SelectedRoom.Value.Playlist[0];
- SelectedRoom.Value.Playlist.RemoveAt(0);
- SelectedRoom.Value.Playlist.Add(item);
- });
+ 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("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2);
}
@@ -117,9 +112,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestNewItemHasNewModInstances()
{
AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
- AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2);
- AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddAssert("item 1 has rate 1.5", () =>
{
@@ -150,7 +145,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
mod = (OsuModDoubleTime)SelectedMods.Value[0];
});
- AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
+ AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
AddAssert("item has rate 1.5", () =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
index d5f53bc354..9420ddf807 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRankRangePill.cs
@@ -9,7 +9,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Tests.Visual.Multiplayer
@@ -18,10 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
private readonly Mock multiplayerClient = new Mock();
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
- // not used directly in component, but required due to it inheriting from OnlinePlayComposite.
- new CachedModelDependencyContainer(base.CreateChildDependencies(parent));
-
[BackgroundDependencyLoader]
private void load()
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
index b53a61f881..195b40bd13 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
SelectedRoom.Value = new Room();
- Child = new StarRatingRangeDisplay
+ Child = new StarRatingRangeDisplay(SelectedRoom.Value)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
@@ -33,11 +33,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
AddStep("set playlist", () =>
{
- SelectedRoom.Value.Playlist.AddRange(new[]
- {
+ SelectedRoom.Value.Playlist =
+ [
new PlaylistItem(new BeatmapInfo { StarRating = min }),
new PlaylistItem(new BeatmapInfo { StarRating = max }),
- });
+ ];
});
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
index 32e90153d8..05136ebee1 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Linq;
using NUnit.Framework;
@@ -29,10 +27,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneTeamVersus : ScreenTestScene
{
- private BeatmapManager beatmaps;
- private BeatmapSetInfo importedSet;
+ private BeatmapManager beatmaps = null!;
+ private BeatmapSetInfo importedSet = null!;
- private TestMultiplayerComponents multiplayerComponents;
+ private TestMultiplayerComponents multiplayerComponents = null!;
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
@@ -64,15 +62,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
createRoom(() => new Room
{
- Name = { Value = "Test Room" },
- Type = { Value = MatchType.TeamVersus },
+ Name = "Test Room",
+ Type = MatchType.TeamVersus,
Playlist =
- {
+ [
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
- }
+ ]
});
AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus);
@@ -84,15 +82,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
createRoom(() => new Room
{
- Name = { Value = "Test Room" },
- Type = { Value = MatchType.TeamVersus },
+ Name = "Test Room",
+ Type = MatchType.TeamVersus,
Playlist =
- {
+ [
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
- }
+ ]
});
AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
@@ -121,25 +119,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
createRoom(() => new Room
{
- Name = { Value = "Test Room" },
- Type = { Value = MatchType.HeadToHead },
+ Name = "Test Room",
+ Type = MatchType.HeadToHead,
Playlist =
- {
+ [
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
- }
+ ]
});
- AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.HeadToHead);
+ AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type == MatchType.HeadToHead);
AddStep("change match type", () => multiplayerClient.ChangeSettings(new MultiplayerRoomSettings
{
MatchType = MatchType.TeamVersus
}).WaitSafely());
- AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.TeamVersus);
+ AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type == MatchType.TeamVersus);
}
[Test]
@@ -147,14 +145,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
createRoom(() => new Room
{
- Name = { Value = "Test Room" },
+ Name = "Test Room",
Playlist =
- {
+ [
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
}
- }
+ ]
});
AddUntilStep("room type is head to head", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.HeadToHead);
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 9f7b20ad43..321f563140 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using NUnit.Framework;
using osu.Framework.Bindables;
@@ -22,7 +20,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
- private TestRoomSettings settings;
+ private TestRoomSettings settings = null!;
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
@@ -47,19 +45,19 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("clear name and beatmap", () =>
{
- SelectedRoom.Value.Name.Value = "";
- SelectedRoom.Value.Playlist.Clear();
+ SelectedRoom.Value.Name = "";
+ SelectedRoom.Value.Playlist = [];
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set name", () => SelectedRoom.Value.Name.Value = "Room name");
+ AddStep("set name", () => SelectedRoom.Value.Name = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
- AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)));
+ AddStep("set beatmap", () => SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
- AddStep("clear name", () => SelectedRoom.Value.Name.Value = "");
+ AddStep("clear name", () => SelectedRoom.Value.Name = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
}
@@ -69,13 +67,13 @@ namespace osu.Game.Tests.Visual.Playlists
const string expected_name = "expected name";
TimeSpan expectedDuration = TimeSpan.FromMinutes(15);
- Room createdRoom = null;
+ Room createdRoom = null!;
AddStep("setup", () =>
{
settings.NameField.Current.Value = expected_name;
settings.DurationField.Current.Value = expectedDuration;
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
+ SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
RoomManager.CreateRequested = r =>
{
@@ -85,8 +83,8 @@ namespace osu.Game.Tests.Visual.Playlists
});
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
- AddAssert("has correct name", () => createdRoom.Name.Value == expected_name);
- AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration);
+ AddAssert("has correct name", () => createdRoom.Name == expected_name);
+ AddAssert("has correct duration", () => createdRoom.Duration == expectedDuration);
}
[Test]
@@ -94,14 +92,14 @@ namespace osu.Game.Tests.Visual.Playlists
{
const string not_found_prefix = "beatmaps not found:";
- string errorMessage = null;
+ string errorMessage = null!;
AddStep("setup", () =>
{
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
- SelectedRoom.Value.Name.Value = "Test Room";
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap));
+ SelectedRoom.Value.Name = "Test Room";
+ SelectedRoom.Value.Playlist = [new PlaylistItem(beatmap)];
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
@@ -127,8 +125,8 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("setup", () =>
{
- SelectedRoom.Value.Name.Value = "Test Room";
- SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
+ SelectedRoom.Value.Name = "Test Room";
+ SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
RoomManager.CreateRequested = _ => failText;
});
@@ -169,7 +167,7 @@ namespace osu.Game.Tests.Visual.Playlists
protected class TestRoomManager : IRoomManager
{
- public Func CreateRequested;
+ public Func? CreateRequested;
public event Action RoomsUpdated
{
@@ -187,7 +185,7 @@ namespace osu.Game.Tests.Visual.Playlists
public void ClearRooms() => throw new NotImplementedException();
- public void CreateRoom(Room room, Action onSuccess = null, Action onError = null)
+ public void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
{
if (CreateRequested == null)
return;
@@ -200,7 +198,7 @@ namespace osu.Game.Tests.Visual.Playlists
onSuccess?.Invoke(room);
}
- public void JoinRoom(Room room, string password, Action onSuccess = null, Action onError = null) => throw new NotImplementedException();
+ 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/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs
index 3b60c28dc0..f72a2cd655 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
@@ -19,17 +20,16 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create list", () =>
{
- SelectedRoom.Value = new Room { RoomID = { Value = 7 } };
-
- for (int i = 0; i < 50; i++)
+ SelectedRoom.Value = new Room
{
- SelectedRoom.Value.RecentParticipants.Add(new APIUser
+ RoomID = 7,
+ RecentParticipants = Enumerable.Range(0, 50).Select(_ => new APIUser
{
Username = "peppy",
Statistics = new UserStatistics { GlobalRank = 1234 },
Id = 2
- });
- }
+ }).ToArray()
+ };
});
}
@@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
- Child = new ParticipantsDisplay(Direction.Horizontal)
+ Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Horizontal)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
AddStep("create component", () =>
{
- Child = new ParticipantsDisplay(Direction.Vertical)
+ Child = new ParticipantsDisplay(SelectedRoom.Value, 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 1636a3d4b8..6a53019b76 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
@@ -1,12 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Diagnostics;
using System.Linq;
-using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -35,11 +32,9 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsRoomCreation : OnlinePlayTestScene
{
- private BeatmapManager manager;
-
- private TestPlaylistsRoomSubScreen match;
-
- private BeatmapSetInfo importedBeatmap;
+ private BeatmapManager manager = null!;
+ private TestPlaylistsRoomSubScreen match = null!;
+ private BeatmapSetInfo importedBeatmap = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -52,11 +47,11 @@ namespace osu.Game.Tests.Visual.Playlists
[SetUpSteps]
public void SetupSteps()
{
- AddStep("set room", () => SelectedRoom!.Value = new Room());
+ AddStep("set room", () => SelectedRoom.Value = new Room());
importBeatmap();
- AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom!.Value)));
+ AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value)));
AddUntilStep("wait for load", () => match.IsCurrentScreen());
}
@@ -65,14 +60,17 @@ namespace osu.Game.Tests.Visual.Playlists
{
setupAndCreateRoom(room =>
{
- room.Name.Value = "my awesome room";
- room.Host.Value = API.LocalUser.Value;
- room.RecentParticipants.Add(room.Host.Value);
- room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
- room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ room.Name = "my awesome room";
+ room.Host = API.LocalUser.Value;
+ room.RecentParticipants = [room.Host];
+ room.EndDate = DateTimeOffset.Now.AddMinutes(5);
+ room.Playlist =
+ [
+ new PlaylistItem(importedBeatmap.Beatmaps.First())
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
AddUntilStep("Progress details are hidden", () => match.ChildrenOfType().FirstOrDefault()?.Parent!.Alpha == 0);
@@ -88,15 +86,18 @@ namespace osu.Game.Tests.Visual.Playlists
{
setupAndCreateRoom(room =>
{
- room.Name.Value = "my awesome room";
- room.MaxAttempts.Value = 5;
- room.Host.Value = API.LocalUser.Value;
- room.RecentParticipants.Add(room.Host.Value);
- room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
- room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ room.Name = "my awesome room";
+ room.MaxAttempts = 5;
+ room.Host = API.LocalUser.Value;
+ room.RecentParticipants = [room.Host];
+ room.EndDate = DateTimeOffset.Now.AddMinutes(5);
+ room.Playlist =
+ [
+ new PlaylistItem(importedBeatmap.Beatmaps.First())
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
AddUntilStep("Progress details are visible", () => match.ChildrenOfType().FirstOrDefault()?.Parent!.Alpha == 1);
@@ -107,21 +108,24 @@ namespace osu.Game.Tests.Visual.Playlists
{
setupAndCreateRoom(room =>
{
- room.Name.Value = "my awesome room";
- room.Host.Value = API.LocalUser.Value;
- room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ room.Name = "my awesome room";
+ room.Host = API.LocalUser.Value;
+ room.Playlist =
+ [
+ new PlaylistItem(importedBeatmap.Beatmaps.First())
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
+ }
+ ];
});
- AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom!.Value.Playlist[0]);
+ AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]);
}
[Test]
public void TestBeatmapUpdatedOnReImport()
{
- string realHash = null;
+ string realHash = null!;
int realOnlineId = 0;
int realOnlineSetId = 0;
@@ -139,40 +143,40 @@ namespace osu.Game.Tests.Visual.Playlists
BeatmapInfo =
{
OnlineID = realOnlineId,
- Metadata = new BeatmapMetadata(),
- BeatmapSet =
- {
- OnlineID = realOnlineSetId
- }
+ Metadata = new BeatmapMetadata()
},
};
+ Debug.Assert(modifiedBeatmap.BeatmapInfo.BeatmapSet != null);
+ modifiedBeatmap.BeatmapInfo.BeatmapSet!.OnlineID = realOnlineSetId;
+
modifiedBeatmap.HitObjects.Clear();
modifiedBeatmap.HitObjects.Add(new HitCircle { StartTime = 5000 });
- Debug.Assert(modifiedBeatmap.BeatmapInfo.BeatmapSet != null);
-
manager.Import(modifiedBeatmap.BeatmapInfo.BeatmapSet);
});
// Create the room using the real beatmap values.
setupAndCreateRoom(room =>
{
- room.Name.Value = "my awesome room";
- room.Host.Value = API.LocalUser.Value;
- room.Playlist.Add(new PlaylistItem(new BeatmapInfo
- {
- MD5Hash = realHash,
- OnlineID = realOnlineId,
- Metadata = new BeatmapMetadata(),
- BeatmapSet = new BeatmapSetInfo
+ room.Name = "my awesome room";
+ room.Host = API.LocalUser.Value;
+ room.Playlist =
+ [
+ new PlaylistItem(new BeatmapInfo
{
- OnlineID = realOnlineSetId,
+ MD5Hash = realHash,
+ OnlineID = realOnlineId,
+ Metadata = new BeatmapMetadata(),
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineID = realOnlineSetId,
+ }
+ })
+ {
+ RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
- })
- {
- RulesetID = new OsuRuleset().RulesetInfo.OnlineID
- });
+ ];
});
AddAssert("match has default beatmap", () => match.Beatmap.IsDefault);
@@ -181,17 +185,11 @@ namespace osu.Game.Tests.Visual.Playlists
{
var originalBeatmap = new TestBeatmap(new OsuRuleset().RulesetInfo)
{
- BeatmapInfo =
- {
- OnlineID = realOnlineId,
- BeatmapSet =
- {
- OnlineID = realOnlineSetId
- }
- },
+ BeatmapInfo = { OnlineID = realOnlineId },
};
Debug.Assert(originalBeatmap.BeatmapInfo.BeatmapSet != null);
+ originalBeatmap.BeatmapInfo.BeatmapSet.OnlineID = realOnlineSetId;
manager.Import(originalBeatmap.BeatmapInfo.BeatmapSet);
});
@@ -201,7 +199,7 @@ namespace osu.Game.Tests.Visual.Playlists
private void setupAndCreateRoom(Action room)
{
- AddStep("setup room", () => room(SelectedRoom!.Value));
+ AddStep("setup room", () => room(SelectedRoom.Value));
AddStep("click create button", () =>
{
@@ -215,8 +213,7 @@ namespace osu.Game.Tests.Visual.Playlists
var beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
Debug.Assert(beatmap.BeatmapInfo.BeatmapSet != null);
-
- importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)?.Value.Detach();
+ importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)!.Value.Detach();
});
private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen
@@ -226,8 +223,7 @@ namespace osu.Game.Tests.Visual.Playlists
public new Bindable Beatmap => base.Beatmap;
[Resolved(canBeNull: true)]
- [CanBeNull]
- private IDialogOverlay dialogOverlay { get; set; }
+ private IDialogOverlay? dialogOverlay { get; set; }
public TestPlaylistsRoomSubScreen(Room room)
: base(room)
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs
index 4925facd8a..c091c089cf 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs
@@ -53,13 +53,13 @@ namespace osu.Game.Tests.Visual.UserInterface
beatmap.OnlineID = 1001;
getRoomRequest.TriggerSuccess(new Room
{
- RoomID = { Value = 1234 },
+ RoomID = 1234,
Playlist =
- {
+ [
new PlaylistItem(beatmap)
- },
- StartDate = { Value = DateTimeOffset.Now.AddMinutes(-5) },
- EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
+ ],
+ StartDate = DateTimeOffset.Now.AddMinutes(-5),
+ EndDate = DateTimeOffset.Now.AddSeconds(30)
});
return true;
@@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.UserInterface
beatmap.OnlineID = 1001;
getRoomRequest.TriggerSuccess(new Room
{
- RoomID = { Value = 1234 },
+ RoomID = 1234,
Playlist =
- {
+ [
new PlaylistItem(beatmap)
- },
- StartDate = { Value = DateTimeOffset.Now.AddMinutes(-50) },
- EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
+ ],
+ StartDate = DateTimeOffset.Now.AddMinutes(-50),
+ EndDate = DateTimeOffset.Now.AddSeconds(30)
});
return true;
diff --git a/osu.Game/.idea/.idea.osu.dir/.idea/.name b/osu.Game/.idea/.idea.osu.dir/.idea/.name
new file mode 100644
index 0000000000..21cb4db60e
--- /dev/null
+++ b/osu.Game/.idea/.idea.osu.dir/.idea/.name
@@ -0,0 +1 @@
+osu
\ No newline at end of file
diff --git a/osu.Game/.idea/.idea.osu.dir/.idea/indexLayout.xml b/osu.Game/.idea/.idea.osu.dir/.idea/indexLayout.xml
new file mode 100644
index 0000000000..7b08163ceb
--- /dev/null
+++ b/osu.Game/.idea/.idea.osu.dir/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game/.idea/.idea.osu.dir/.idea/vcs.xml b/osu.Game/.idea/.idea.osu.dir/.idea/vcs.xml
new file mode 100644
index 0000000000..6c0b863585
--- /dev/null
+++ b/osu.Game/.idea/.idea.osu.dir/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game/.idea/.idea.osu.dir/.idea/workspace.xml b/osu.Game/.idea/.idea.osu.dir/.idea/workspace.xml
new file mode 100644
index 0000000000..4be7e05a9a
--- /dev/null
+++ b/osu.Game/.idea/.idea.osu.dir/.idea/workspace.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
index 6f71fa90b8..be6ca43f4b 100644
--- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
+++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs
@@ -14,9 +14,17 @@ namespace osu.Game.Beatmaps.Drawables
///
public partial class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable
{
- public readonly Bindable Beatmap = new Bindable();
+ public readonly Bindable Beatmap = new Bindable();
- protected override double LoadDelay => 500;
+ ///
+ /// Delay before the background is loaded while on-screen.
+ ///
+ public double BackgroundLoadDelay { get; set; } = 500;
+
+ ///
+ /// Delay before the background is unloaded while off-screen.
+ ///
+ public double BackgroundUnloadDelay { get; set; } = 10000;
[Resolved]
private BeatmapManager beatmaps { get; set; } = null!;
@@ -29,10 +37,9 @@ namespace osu.Game.Beatmaps.Drawables
this.beatmapSetCoverType = beatmapSetCoverType;
}
- ///
- /// Delay before the background is unloaded while off-screen.
- ///
- protected virtual double UnloadDelay => 10000;
+ protected override double LoadDelay => BackgroundLoadDelay;
+
+ protected virtual double UnloadDelay => BackgroundUnloadDelay;
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) =>
new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay) { RelativeSizeAxes = Axes.Both };
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index ff147aba10..998a34931d 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
@@ -181,10 +180,10 @@ namespace osu.Game.Online.Multiplayer
await joinOrLeaveTaskChain.Add(async () =>
{
- Debug.Assert(room.RoomID.Value != null);
+ Debug.Assert(room.RoomID != null);
// Join the server-side room.
- var joinedRoom = await JoinRoom(room.RoomID.Value.Value, password ?? room.Password.Value).ConfigureAwait(false);
+ var joinedRoom = await JoinRoom(room.RoomID.Value, password ?? room.Password).ConfigureAwait(false);
Debug.Assert(joinedRoom != null);
// Populate users.
@@ -201,12 +200,11 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(joinedRoom.Playlist.Count > 0);
- APIRoom.Playlist.Clear();
- APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(item => new PlaylistItem(item)));
- APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId);
+ 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.Value = null;
+ APIRoom.EndDate = null;
Debug.Assert(LocalUser != null);
addUserToAPIRoom(LocalUser);
@@ -397,15 +395,15 @@ namespace osu.Game.Online.Multiplayer
switch (state)
{
case MultiplayerRoomState.Open:
- APIRoom.Status.Value = APIRoom.HasPassword.Value ? new RoomStatusOpenPrivate() : new RoomStatusOpen();
+ APIRoom.Status = APIRoom.HasPassword ? new RoomStatusOpenPrivate() : new RoomStatusOpen();
break;
case MultiplayerRoomState.Playing:
- APIRoom.Status.Value = new RoomStatusPlaying();
+ APIRoom.Status = new RoomStatusPlaying();
break;
case MultiplayerRoomState.Closed:
- APIRoom.Status.Value = new RoomStatusEnded();
+ APIRoom.Status = new RoomStatusEnded();
break;
}
@@ -459,7 +457,7 @@ namespace osu.Game.Online.Multiplayer
if (apiUser == null || apiRoom == null) return;
PostNotification?.Invoke(
- new UserAvatarNotification(apiUser, NotificationsStrings.InvitedYouToTheMultiplayer(apiUser.Username, apiRoom.Name.Value))
+ new UserAvatarNotification(apiUser, NotificationsStrings.InvitedYouToTheMultiplayer(apiUser.Username, apiRoom.Name))
{
Activated = () =>
{
@@ -487,12 +485,12 @@ namespace osu.Game.Online.Multiplayer
{
Debug.Assert(APIRoom != null);
- APIRoom.RecentParticipants.Add(user.User ?? new APIUser
+ APIRoom.RecentParticipants = APIRoom.RecentParticipants.Append(user.User ?? new APIUser
{
Id = user.UserID,
Username = "[Unresolved]"
- });
- APIRoom.ParticipantCount.Value++;
+ }).ToArray();
+ APIRoom.ParticipantCount++;
}
private Task handleUserLeft(MultiplayerRoomUser user, Action? callback)
@@ -506,8 +504,8 @@ namespace osu.Game.Online.Multiplayer
PlayingUserIds.Remove(user.UserID);
Debug.Assert(APIRoom != null);
- APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID);
- APIRoom.ParticipantCount.Value--;
+ APIRoom.RecentParticipants = APIRoom.RecentParticipants.Where(u => u.Id != user.UserID).ToArray();
+ APIRoom.ParticipantCount--;
callback?.Invoke(user);
RoomUpdated?.Invoke();
@@ -528,7 +526,7 @@ namespace osu.Game.Online.Multiplayer
var user = Room.Users.FirstOrDefault(u => u.UserID == userId);
Room.Host = user;
- APIRoom.Host.Value = user?.User;
+ APIRoom.Host = user?.User;
RoomUpdated?.Invoke();
}, false);
@@ -734,7 +732,7 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(APIRoom != null);
Room.Playlist.Add(item);
- APIRoom.Playlist.Add(new PlaylistItem(item));
+ APIRoom.Playlist = APIRoom.Playlist.Append(new PlaylistItem(item)).ToArray();
ItemAdded?.Invoke(item);
RoomUpdated?.Invoke();
@@ -753,7 +751,7 @@ namespace osu.Game.Online.Multiplayer
Debug.Assert(APIRoom != null);
Room.Playlist.Remove(Room.Playlist.Single(existing => existing.ID == playlistItemId));
- APIRoom.Playlist.RemoveAll(existing => existing.ID == playlistItemId);
+ APIRoom.Playlist = APIRoom.Playlist.Where(i => i.ID != playlistItemId).ToArray();
Debug.Assert(Room.Playlist.Count > 0);
@@ -771,30 +769,10 @@ namespace osu.Game.Online.Multiplayer
if (Room == null)
return;
- try
- {
- Debug.Assert(APIRoom != null);
+ Debug.Assert(APIRoom != null);
- Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;
-
- int existingIndex = APIRoom.Playlist.IndexOf(APIRoom.Playlist.Single(existing => existing.ID == item.ID));
-
- APIRoom.Playlist.RemoveAt(existingIndex);
- APIRoom.Playlist.Insert(existingIndex, new PlaylistItem(item));
- }
- catch (Exception ex)
- {
- // Temporary code to attempt to figure out long-term failing tests.
- StringBuilder exceptionText = new StringBuilder();
-
- exceptionText.AppendLine("MultiplayerClient test failure investigation");
- exceptionText.AppendLine($"Exception : {ex.ToString()}");
- exceptionText.AppendLine($"Lookup : {item.ID}");
- exceptionText.AppendLine($"Items in Room.Playlist : {string.Join(',', Room.Playlist.Select(i => i.ID))}");
- exceptionText.AppendLine($"Items in APIRoom.Playlist: {string.Join(',', APIRoom!.Playlist.Select(i => i.ID))}");
-
- throw new AggregateException(exceptionText.ToString());
- }
+ Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;
+ APIRoom.Playlist = APIRoom.Playlist.Select((pi, i) => pi.ID == item.ID ? new PlaylistItem(item) : APIRoom.Playlist[i]).ToArray();
ItemChanged?.Invoke(item);
RoomUpdated?.Invoke();
@@ -841,14 +819,14 @@ namespace osu.Game.Online.Multiplayer
// Update a few properties of the room instantaneously.
Room.Settings = settings;
- APIRoom.Name.Value = Room.Settings.Name;
- APIRoom.Password.Value = Room.Settings.Password;
- APIRoom.Status.Value = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate();
- APIRoom.Type.Value = Room.Settings.MatchType;
- APIRoom.QueueMode.Value = Room.Settings.QueueMode;
- APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
- APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
- APIRoom.AutoSkip.Value = Room.Settings.AutoSkip;
+ APIRoom.Name = Room.Settings.Name;
+ APIRoom.Password = Room.Settings.Password;
+ APIRoom.Status = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate();
+ APIRoom.Type = Room.Settings.MatchType;
+ APIRoom.QueueMode = Room.Settings.QueueMode;
+ APIRoom.AutoStartDuration = Room.Settings.AutoStartDuration;
+ APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
+ APIRoom.AutoSkip = Room.Settings.AutoSkip;
RoomUpdated?.Invoke();
}
diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs
index 1b5e08c729..cd797a9668 100644
--- a/osu.Game/Online/Rooms/GetRoomsRequest.cs
+++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs
@@ -44,12 +44,12 @@ namespace osu.Game.Online.Rooms
// API doesn't populate status so let's do it here.
foreach (var room in Response)
{
- if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value)
- room.Status.Value = new RoomStatusEnded();
- else if (room.HasPassword.Value)
- room.Status.Value = new RoomStatusOpenPrivate();
+ if (room.EndDate != null && DateTimeOffset.Now >= room.EndDate)
+ room.Status = new RoomStatusEnded();
+ else if (room.HasPassword)
+ room.Status = new RoomStatusOpenPrivate();
else
- room.Status.Value = new RoomStatusOpen();
+ room.Status = new RoomStatusOpen();
}
}
}
diff --git a/osu.Game/Online/Rooms/JoinRoomRequest.cs b/osu.Game/Online/Rooms/JoinRoomRequest.cs
index 9a73104b60..2ceb37fc01 100644
--- a/osu.Game/Online/Rooms/JoinRoomRequest.cs
+++ b/osu.Game/Online/Rooms/JoinRoomRequest.cs
@@ -27,6 +27,6 @@ namespace osu.Game.Online.Rooms
return req;
}
- protected override string Target => $@"rooms/{Room.RoomID.Value}/users/{User!.Id}";
+ protected override string Target => $@"rooms/{Room.RoomID}/users/{User!.Id}";
}
}
diff --git a/osu.Game/Online/Rooms/PartRoomRequest.cs b/osu.Game/Online/Rooms/PartRoomRequest.cs
index 2416833a1e..77b5619efb 100644
--- a/osu.Game/Online/Rooms/PartRoomRequest.cs
+++ b/osu.Game/Online/Rooms/PartRoomRequest.cs
@@ -23,6 +23,6 @@ namespace osu.Game.Online.Rooms
return req;
}
- protected override string Target => $"rooms/{room.RoomID.Value}/users/{User!.Id}";
+ protected override string Target => $"rooms/{room.RoomID}/users/{User!.Id}";
}
}
diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs
index 8591b5bb47..8afa7d90f8 100644
--- a/osu.Game/Online/Rooms/PlaylistExtensions.cs
+++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using Humanizer;
using Humanizer.Localisation;
-using osu.Framework.Bindables;
using osu.Game.Rulesets;
using osu.Game.Utils;
@@ -30,7 +29,7 @@ namespace osu.Game.Online.Rooms
/// or the last-played if all items are expired,
/// or if was empty.
///
- public static PlaylistItem? GetCurrentItem(this ICollection playlist)
+ public static PlaylistItem? GetCurrentItem(this IReadOnlyCollection playlist)
{
if (playlist.Count == 0)
return null;
@@ -43,7 +42,7 @@ namespace osu.Game.Online.Rooms
///
/// Returns the total duration from the in playlist order from the supplied ,
///
- public static string GetTotalDuration(this BindableList playlist, RulesetStore rulesetStore) =>
+ public static string GetTotalDuration(this IReadOnlyList playlist, RulesetStore rulesetStore) =>
playlist.Select(p =>
{
double rate = 1;
diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs
index a900d8f3d7..47d4e163bf 100644
--- a/osu.Game/Online/Rooms/PlaylistItem.cs
+++ b/osu.Game/Online/Rooms/PlaylistItem.cs
@@ -120,18 +120,21 @@ namespace osu.Game.Online.Rooms
#endregion
- public PlaylistItem With(Optional beatmap = default, Optional playlistOrder = default) => new PlaylistItem(beatmap.GetOr(Beatmap))
+ public PlaylistItem With(Optional id = default, Optional beatmap = default, Optional playlistOrder = default)
{
- ID = ID,
- OwnerID = OwnerID,
- RulesetID = RulesetID,
- Expired = Expired,
- PlaylistOrder = playlistOrder.GetOr(PlaylistOrder),
- PlayedAt = PlayedAt,
- AllowedMods = AllowedMods,
- RequiredMods = RequiredMods,
- valid = { Value = Valid.Value },
- };
+ return new PlaylistItem(beatmap.GetOr(Beatmap))
+ {
+ ID = id.GetOr(ID),
+ OwnerID = OwnerID,
+ RulesetID = RulesetID,
+ Expired = Expired,
+ PlaylistOrder = playlistOrder.GetOr(PlaylistOrder),
+ PlayedAt = PlayedAt,
+ AllowedMods = AllowedMods,
+ RequiredMods = RequiredMods,
+ valid = { Value = Valid.Value },
+ };
+ }
public bool Equals(PlaylistItem? other)
=> ID == other?.ID
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index c39932c3bf..a26a3fcb34 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -1,13 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
+using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
+using System.Runtime.CompilerServices;
using Newtonsoft.Json;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Game.IO.Serialization.Converters;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
@@ -16,162 +15,332 @@ using osu.Game.Online.Rooms.RoomStatuses;
namespace osu.Game.Online.Rooms
{
[JsonObject(MemberSerialization.OptIn)]
- public partial class Room : IDependencyInjectionCandidate
+ public partial class Room : INotifyPropertyChanged
{
- [Cached]
- [JsonProperty("id")]
- public readonly Bindable RoomID = new Bindable();
+ public event PropertyChangedEventHandler? PropertyChanged;
- [Cached]
- [JsonProperty("name")]
- public readonly Bindable Name = new Bindable();
-
- [Cached]
- [JsonProperty("host")]
- public readonly Bindable Host = new Bindable();
-
- [Cached]
- [JsonProperty("playlist")]
- public readonly BindableList Playlist = new BindableList();
-
- [Cached]
- [JsonProperty("channel_id")]
- public readonly Bindable ChannelId = new Bindable();
-
- [JsonProperty("current_playlist_item")]
- [Cached]
- public readonly Bindable CurrentPlaylistItem = new Bindable();
-
- [JsonProperty("playlist_item_stats")]
- [Cached]
- public readonly Bindable PlaylistItemStats = new Bindable();
-
- [JsonProperty("difficulty_range")]
- [Cached]
- public readonly Bindable DifficultyRange = new Bindable();
-
- [Cached]
- public readonly Bindable Category = new Bindable();
-
- // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
- [JsonProperty("category")]
- [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
- private RoomCategory category
+ ///
+ /// The online room ID. Will be null while the room has not yet been created.
+ ///
+ public long? RoomID
{
- get => Category.Value;
- set => Category.Value = value;
+ get => roomId;
+ set => SetField(ref roomId, value);
}
- [Cached]
- public readonly Bindable MaxAttempts = new Bindable();
-
- [Cached]
- public readonly Bindable Status = new Bindable(new RoomStatusOpen());
-
- [Cached]
- public readonly Bindable Availability = new Bindable();
-
- [Cached]
- public readonly Bindable Type = new Bindable();
-
- // Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
- [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
- [JsonProperty("type")]
- private MatchType type
+ ///
+ /// The room name.
+ ///
+ public string Name
{
- get => Type.Value;
- set => Type.Value = value;
+ get => name;
+ set => SetField(ref name, value);
}
- [Cached]
- public readonly Bindable QueueMode = new Bindable();
-
- [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
- [JsonProperty("queue_mode")]
- private QueueMode queueMode
+ ///
+ /// Sets the room password. Will be null after the room is created.
+ ///
+ ///
+ /// To check if the room has a password, use .
+ ///
+ public string? Password
{
- get => QueueMode.Value;
- set => QueueMode.Value = value;
- }
-
- [Cached]
- public readonly Bindable AutoStartDuration = new Bindable();
-
- [JsonProperty("auto_start_duration")]
- private ushort autoStartDuration
- {
- get => (ushort)AutoStartDuration.Value.TotalSeconds;
- set => AutoStartDuration.Value = TimeSpan.FromSeconds(value);
- }
-
- [Cached]
- public readonly Bindable MaxParticipants = new Bindable();
-
- [Cached]
- [JsonProperty("current_user_score")]
- public readonly Bindable UserScore = new Bindable();
-
- [JsonProperty("has_password")]
- public readonly Bindable HasPassword = new Bindable();
-
- [Cached]
- [JsonProperty("recent_participants")]
- public readonly BindableList RecentParticipants = new BindableList();
-
- [Cached]
- [JsonProperty("participant_count")]
- public readonly Bindable ParticipantCount = new Bindable();
-
- #region Properties only used for room creation request
-
- [Cached(Name = nameof(Password))]
- [JsonProperty("password")]
- public readonly Bindable Password = new Bindable();
-
- [Cached]
- public readonly Bindable Duration = new Bindable();
-
- [JsonProperty("duration")]
- private int? duration
- {
- get => (int?)Duration.Value?.TotalMinutes;
+ get => password;
set
{
- if (value == null)
- Duration.Value = null;
- else
- Duration.Value = TimeSpan.FromMinutes(value.Value);
+ SetField(ref password, value);
+ HasPassword = !string.IsNullOrEmpty(value);
}
}
- #endregion
+ ///
+ /// Whether the room has a password.
+ ///
+ ///
+ /// To set a password, use .
+ ///
+ [JsonProperty("has_password")]
+ public bool HasPassword
+ {
+ get => hasPassword;
+ private set => SetField(ref hasPassword, value);
+ }
+
+ ///
+ /// The room host. Will be null while the room has not yet been created.
+ ///
+ public APIUser? Host
+ {
+ get => host;
+ set => SetField(ref host, value);
+ }
+
+ ///
+ /// The room category.
+ ///
+ public RoomCategory Category
+ {
+ get => category;
+ set => SetField(ref category, value);
+ }
+
+ ///
+ /// The duration for which the room will be open. Will be null after the room is created.
+ ///
+ ///
+ /// To check the room end time, use .
+ ///
+ public TimeSpan? Duration
+ {
+ get => duration == null ? null : TimeSpan.FromMinutes(duration.Value);
+ set => SetField(ref duration, value == null ? null : (int)value.Value.TotalMinutes);
+ }
+
+ ///
+ /// The date at which the room was opened. Will be null while the room has not yet been created.
+ ///
+ public DateTimeOffset? StartDate
+ {
+ get => startDate;
+ set => SetField(ref startDate, value);
+ }
+
+ ///
+ /// The date at which the room will be closed.
+ ///
+ ///
+ /// To set the room duration, use .
+ ///
+ public DateTimeOffset? EndDate
+ {
+ get => endDate;
+ set => SetField(ref endDate, value);
+ }
+
+ ///
+ /// The maximum number of users allowed in the room.
+ ///
+ public int? MaxParticipants
+ {
+ get => maxParticipants;
+ set => SetField(ref maxParticipants, value);
+ }
+
+ ///
+ /// The current number of users in the room.
+ ///
+ public int ParticipantCount
+ {
+ get => participantCount;
+ set => SetField(ref participantCount, value);
+ }
+
+ ///
+ /// The set of most recent participants in the room.
+ ///
+ public IReadOnlyList RecentParticipants
+ {
+ get => recentParticipants;
+ set => SetList(ref recentParticipants, value);
+ }
+
+ ///
+ /// The match type.
+ ///
+ public MatchType Type
+ {
+ get => type;
+ set => SetField(ref type, value);
+ }
+
+ ///
+ /// The maximum number of attempts on the playlist. Only valid for playlist rooms.
+ ///
+ public int? MaxAttempts
+ {
+ get => maxAttempts;
+ set => SetField(ref maxAttempts, value);
+ }
+
+ ///
+ /// The room playlist.
+ ///
+ public IReadOnlyList Playlist
+ {
+ get => playlist;
+ set => SetList(ref playlist, value);
+ }
+
+ ///
+ /// Describes the items in the playlist.
+ ///
+ public RoomPlaylistItemStats? PlaylistItemStats
+ {
+ get => playlistItemStats;
+ set => SetField(ref playlistItemStats, value);
+ }
+
+ ///
+ /// Describes the range of difficulty of the room.
+ ///
+ public RoomDifficultyRange? DifficultyRange
+ {
+ get => difficultyRange;
+ set => SetField(ref difficultyRange, value);
+ }
+
+ ///
+ /// The playlist queueing mode. Only valid for multiplayer rooms.
+ ///
+ public QueueMode QueueMode
+ {
+ get => queueMode;
+ set => SetField(ref queueMode, value);
+ }
+
+ ///
+ /// Whether to automatically skip map intros. Only valid for multiplayer rooms.
+ ///
+ public bool AutoSkip
+ {
+ get => autoSkip;
+ set => SetField(ref autoSkip, value);
+ }
+
+ ///
+ /// The amount of time before the match is automatically started. Only valid for multiplayer rooms.
+ ///
+ public TimeSpan AutoStartDuration
+ {
+ get => TimeSpan.FromSeconds(autoStartDuration);
+ set => SetField(ref autoStartDuration, (ushort)value.TotalSeconds);
+ }
+
+ ///
+ /// Provides some extra scoring statistics for the local user in the room.
+ ///
+ public PlaylistAggregateScore? UserScore
+ {
+ get => userScore;
+ set => SetField(ref userScore, value);
+ }
+
+ ///
+ /// Represents the current item selected within the room.
+ ///
+ ///
+ /// Only valid for room listing requests (i.e. in the lounge screen), and may not be valid while inside the room.
+ ///
+ public PlaylistItem? CurrentPlaylistItem
+ {
+ get => currentPlaylistItem;
+ set => SetField(ref currentPlaylistItem, value);
+ }
+
+ ///
+ /// The chat channel id for the room. Will be 0 while the room has not yet been created.
+ ///
+ public int ChannelId
+ {
+ get => channelId;
+ private set => SetField(ref channelId, value);
+ }
+
+ ///
+ /// The current room status.
+ ///
+ public RoomStatus Status
+ {
+ get => status;
+ set => SetField(ref status, value);
+ }
+
+ ///
+ /// Describes which players are able to join the room.
+ ///
+ public RoomAvailability Availability
+ {
+ get => availability;
+ set => SetField(ref availability, value);
+ }
+
+ [JsonProperty("id")]
+ private long? roomId;
+
+ [JsonProperty("name")]
+ private string name = string.Empty;
+
+ [JsonProperty("password")]
+ private string? password;
+
+ // Not serialised (internal use only).
+ private bool hasPassword;
+
+ [JsonProperty("host")]
+ private APIUser? host;
+
+ [JsonProperty("category")]
+ [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
+ private RoomCategory category;
+
+ [JsonProperty("duration")]
+ private int? duration;
- // Only supports retrieval for now
- [Cached]
[JsonProperty("starts_at")]
- public readonly Bindable StartDate = new Bindable();
+ private DateTimeOffset? startDate;
- // Only supports retrieval for now
- [Cached]
[JsonProperty("ends_at")]
- public readonly Bindable EndDate = new Bindable();
+ private DateTimeOffset? endDate;
+
+ // Not yet serialised (not implemented).
+ private int? maxParticipants;
+
+ [JsonProperty("participant_count")]
+ private int participantCount;
+
+ [JsonProperty("recent_participants")]
+ private IReadOnlyList recentParticipants = [];
- // Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930)
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
- private int? maxAttempts
- {
- get => MaxAttempts.Value;
- set => MaxAttempts.Value = value;
- }
+ private int? maxAttempts;
+
+ [JsonProperty("playlist")]
+ private IReadOnlyList playlist = [];
+
+ [JsonProperty("playlist_item_stats")]
+ private RoomPlaylistItemStats? playlistItemStats;
+
+ [JsonProperty("difficulty_range")]
+ private RoomDifficultyRange? difficultyRange;
+
+ [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
+ [JsonProperty("type")]
+ private MatchType type;
+
+ [JsonConverter(typeof(SnakeCaseStringEnumConverter))]
+ [JsonProperty("queue_mode")]
+ private QueueMode queueMode;
- [Cached]
[JsonProperty("auto_skip")]
- public readonly Bindable AutoSkip = new Bindable();
+ private bool autoSkip;
- public Room()
- {
- Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
- }
+ [JsonProperty("auto_start_duration")]
+ private ushort autoStartDuration;
+
+ [JsonProperty("current_user_score")]
+ private PlaylistAggregateScore? userScore;
+
+ [JsonProperty("current_playlist_item")]
+ private PlaylistItem? currentPlaylistItem;
+
+ [JsonProperty("channel_id")]
+ private int channelId;
+
+ // Not serialised (see: GetRoomsRequest).
+ private RoomStatus status = new RoomStatusOpen();
+
+ // Not yet serialised (not implemented).
+ private RoomAvailability availability;
///
/// Copies values from another into this one.
@@ -182,43 +351,34 @@ namespace osu.Game.Online.Rooms
/// The to copy values from.
public void CopyFrom(Room other)
{
- RoomID.Value = other.RoomID.Value;
- Name.Value = other.Name.Value;
+ RoomID = other.RoomID;
+ Name = other.Name;
- Category.Value = other.Category.Value;
+ Category = other.Category;
- if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
- Host.Value = other.Host.Value;
+ if (other.Host != null && Host?.Id != other.Host.Id)
+ Host = other.Host;
- ChannelId.Value = other.ChannelId.Value;
- Status.Value = other.Status.Value;
- Availability.Value = other.Availability.Value;
- HasPassword.Value = other.HasPassword.Value;
- Type.Value = other.Type.Value;
- MaxParticipants.Value = other.MaxParticipants.Value;
- ParticipantCount.Value = other.ParticipantCount.Value;
- EndDate.Value = other.EndDate.Value;
- UserScore.Value = other.UserScore.Value;
- QueueMode.Value = other.QueueMode.Value;
- AutoStartDuration.Value = other.AutoStartDuration.Value;
- DifficultyRange.Value = other.DifficultyRange.Value;
- PlaylistItemStats.Value = other.PlaylistItemStats.Value;
- CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value;
- AutoSkip.Value = other.AutoSkip.Value;
+ ChannelId = other.ChannelId;
+ Status = other.Status;
+ Availability = other.Availability;
+ HasPassword = other.HasPassword;
+ Type = other.Type;
+ MaxParticipants = other.MaxParticipants;
+ ParticipantCount = other.ParticipantCount;
+ EndDate = other.EndDate;
+ UserScore = other.UserScore;
+ QueueMode = other.QueueMode;
+ AutoStartDuration = other.AutoStartDuration;
+ DifficultyRange = other.DifficultyRange;
+ PlaylistItemStats = other.PlaylistItemStats;
+ CurrentPlaylistItem = other.CurrentPlaylistItem;
+ AutoSkip = other.AutoSkip;
other.RemoveExpiredPlaylistItems();
- if (!Playlist.SequenceEqual(other.Playlist))
- {
- Playlist.Clear();
- Playlist.AddRange(other.Playlist);
- }
-
- if (!RecentParticipants.SequenceEqual(other.RecentParticipants))
- {
- RecentParticipants.Clear();
- RecentParticipants.AddRange(other.RecentParticipants);
- }
+ Playlist = other.Playlist;
+ RecentParticipants = other.RecentParticipants;
}
public void RemoveExpiredPlaylistItems()
@@ -226,8 +386,8 @@ namespace osu.Game.Online.Rooms
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
- if (!(Status.Value is RoomStatusEnded))
- Playlist.RemoveAll(i => i.Expired);
+ if (Status is not RoomStatusEnded)
+ Playlist = Playlist.Where(i => !i.Expired).ToArray();
}
[JsonObject(MemberSerialization.OptIn)]
@@ -240,7 +400,7 @@ namespace osu.Game.Online.Rooms
public int CountTotal;
[JsonProperty("ruleset_ids")]
- public int[] RulesetIDs;
+ public int[] RulesetIDs = [];
}
[JsonObject(MemberSerialization.OptIn)]
@@ -252,5 +412,28 @@ namespace osu.Game.Online.Rooms
[JsonProperty("max")]
public double Max;
}
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null!)
+ => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+
+ protected bool SetList(ref IReadOnlyList list, IReadOnlyList value, [CallerMemberName] string propertyName = null!)
+ {
+ if (list.SequenceEqual(value))
+ return false;
+
+ list = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+
+ protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null!)
+ {
+ if (EqualityComparer.Default.Equals(field, value))
+ return false;
+
+ field = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
}
}
diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs
index 44a53efa7b..be22fc3c30 100644
--- a/osu.Game/Screens/Menu/DailyChallengeButton.cs
+++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs
@@ -156,15 +156,15 @@ namespace osu.Game.Screens.Menu
Room = room;
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
- if (room.StartDate.Value != null && room.RoomID.Value != lastDailyChallengeRoomID)
+ if (room.StartDate != null && room.RoomID != lastDailyChallengeRoomID)
{
- lastDailyChallengeRoomID = room.RoomID.Value;
+ lastDailyChallengeRoomID = room.RoomID;
// new challenge is live, reset intro played static.
statics.SetValue(Static.DailyChallengeIntroPlayed, false);
// we only want to notify the user if the new challenge just went live.
- if (Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800)
+ if (Math.Abs((DateTimeOffset.Now - room.StartDate.Value).TotalSeconds) < 1800)
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
}
@@ -180,7 +180,7 @@ namespace osu.Game.Screens.Menu
if (Room == null)
return;
- var remaining = (Room.EndDate.Value - DateTimeOffset.Now) ?? TimeSpan.Zero;
+ var remaining = (Room.EndDate - DateTimeOffset.Now) ?? TimeSpan.Zero;
if (remaining <= TimeSpan.Zero)
{
diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
index 7c57f5b4f5..5c8ac5ce73 100644
--- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs
@@ -1,32 +1,40 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class BeatmapTitle : OnlinePlayComposite
+ public partial class BeatmapTitle : CompositeDrawable
{
+ private readonly Room room;
private readonly LinkFlowContainer textFlow;
- public BeatmapTitle()
- {
- AutoSizeAxes = Axes.Both;
+ [Resolved]
+ private OsuColour colours { get; set; } = null!;
+ public BeatmapTitle(Room room)
+ {
+ this.room = room;
+
+ AutoSizeAxes = Axes.Both;
InternalChild = textFlow = new LinkFlowContainer { AutoSizeAxes = Axes.Both };
}
- [BackgroundDependencyLoader]
- private void load()
+ protected override void LoadComplete()
{
- Playlist.CollectionChanged += (_, _) => updateText();
+ base.LoadComplete();
+ room.PropertyChanged += onRoomPropertyChanged;
updateText();
}
@@ -46,8 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
}
- [Resolved]
- private OsuColour colours { get; set; } = null!;
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ updateText();
+ }
private void updateText()
{
@@ -56,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
textFlow.Clear();
- var beatmap = Playlist.FirstOrDefault()?.Beatmap;
+ var beatmap = room.Playlist.FirstOrDefault()?.Beatmap;
if (beatmap == null)
{
@@ -78,5 +89,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.OnlineID.ToString(), "Open beatmap");
}
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
index 4b38ea68b3..67c3586a35 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
@@ -35,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
});
}
- private GetRoomsRequest lastPollRequest;
+ private GetRoomsRequest? lastPollRequest;
protected override Task Poll()
{
@@ -53,11 +51,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
req.Success += result =>
{
- result = result.Where(r => r.Category.Value != RoomCategory.DailyChallenge).ToList();
+ result = result.Where(r => r.Category != RoomCategory.DailyChallenge).ToList();
foreach (var existing in RoomManager.Rooms.ToArray())
{
- if (result.All(r => r.RoomID.Value != existing.RoomID.Value))
+ if (result.All(r => r.RoomID != existing.RoomID))
RoomManager.RemoveRoom(existing);
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs b/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs
index b0ede8d9b5..1f2b2e3fc2 100644
--- a/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/MatchBeatmapDetailArea.cs
@@ -1,12 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
+using System.ComponentModel;
using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterfaceV2;
@@ -14,23 +11,22 @@ using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Select;
using osuTK;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Components
{
public partial class MatchBeatmapDetailArea : BeatmapDetailArea
{
- public Action CreateNewItem;
-
- public readonly Bindable SelectedItem = new Bindable();
-
- [Resolved(typeof(Room))]
- protected BindableList Playlist { get; private set; }
+ public Action? CreateNewItem;
+ private readonly Room room;
private readonly GridContainer playlistArea;
private readonly DrawableRoomPlaylist playlist;
- public MatchBeatmapDetailArea()
+ public MatchBeatmapDetailArea(Room room)
{
+ this.room = room;
+
Add(playlistArea = new GridContainer
{
RelativeSizeAxes = Axes.Both,
@@ -72,10 +68,21 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
base.LoadComplete();
- playlist.Items.BindTo(Playlist);
- playlist.SelectedItem.BindTo(SelectedItem);
+ playlist.Items.BindCollectionChanged((_, __) => room.Playlist = playlist.Items.ToArray());
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomPlaylist();
}
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ updateRoomPlaylist();
+ }
+
+ private void updateRoomPlaylist()
+ => playlist.Items.ReplaceRange(0, playlist.Items.Count, room.Playlist);
+
protected override void OnTabChanged(BeatmapDetailAreaTabItem tab, bool selectedMods)
{
base.OnTabChanged(tab, selectedMods);
@@ -93,5 +100,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
protected override BeatmapDetailAreaTabItem[] CreateTabItems() => base.CreateTabItems().Prepend(new BeatmapDetailAreaPlaylistTabItem()).ToArray();
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs
deleted file mode 100644
index 0d4cd30090..0000000000
--- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-#nullable disable
-
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Game.Beatmaps.Drawables;
-using osu.Game.Online.Rooms;
-
-namespace osu.Game.Screens.OnlinePlay.Components
-{
- public partial class OnlinePlayBackgroundSprite : OnlinePlayComposite
- {
- protected readonly BeatmapSetCoverType BeatmapSetCoverType;
- private UpdateableBeatmapBackgroundSprite sprite;
-
- public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
- {
- BeatmapSetCoverType = beatmapSetCoverType;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- InternalChild = sprite = CreateBackgroundSprite();
-
- CurrentPlaylistItem.BindValueChanged(_ => updateBeatmap());
- Playlist.CollectionChanged += (_, _) => updateBeatmap();
-
- updateBeatmap();
- }
-
- private void updateBeatmap()
- {
- sprite.Beatmap.Value = CurrentPlaylistItem.Value?.Beatmap ?? Playlist.GetCurrentItem()?.Beatmap;
- }
-
- protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs
index 09a3602cdd..d9cdcac7d7 100644
--- a/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedHeader.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
///
/// A header used in the multiplayer interface which shows text / details beneath a line.
///
- public partial class OverlinedHeader : OnlinePlayComposite
+ public partial class OverlinedHeader : CompositeDrawable
{
private bool showLine = true;
diff --git a/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs b/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs
index dd728e460b..55d9f273e9 100644
--- a/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/OverlinedPlaylistHeader.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.ComponentModel;
using osu.Framework.Allocation;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
@@ -9,19 +10,38 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
public partial class OverlinedPlaylistHeader : OverlinedHeader
{
+ private readonly Room room;
+
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
- public OverlinedPlaylistHeader()
+ public OverlinedPlaylistHeader(Room room)
: base("Playlist")
{
+ this.room = room;
}
protected override void LoadComplete()
{
base.LoadComplete();
- Playlist.BindCollectionChanged((_, _) => Details.Value = Playlist.GetTotalDuration(rulesets), true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateDuration();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ updateDuration();
+ }
+
+ private void updateDuration()
+ => Details.Value = room.Playlist.GetTotalDuration(rulesets);
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs
index 9f7e700ab3..db9cf3f92d 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs
@@ -1,33 +1,36 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class ParticipantCountDisplay : OnlinePlayComposite
+ public partial class ParticipantCountDisplay : CompositeDrawable
{
private const float text_size = 30;
private const float transition_duration = 100;
- private OsuSpriteText slash, maxText;
+ private readonly Room room;
- public ParticipantCountDisplay()
+ private OsuSpriteText slash = null!;
+ private OsuSpriteText maxText = null!;
+ private OsuSpriteText count = null!;
+
+ public ParticipantCountDisplay(Room room)
{
+ this.room = room;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
- OsuSpriteText count;
-
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
@@ -50,14 +53,33 @@ namespace osu.Game.Screens.OnlinePlay.Components
},
}
};
-
- MaxParticipants.BindValueChanged(_ => updateMax(), true);
- ParticipantCount.BindValueChanged(c => count.Text = c.NewValue.ToString("#,0"), true);
}
- private void updateMax()
+ protected override void LoadComplete()
{
- if (MaxParticipants.Value == null)
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomParticipantCount();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.MaxParticipants):
+ updateRoomMaxParticipants();
+ break;
+
+ case nameof(Room.ParticipantCount):
+ updateRoomParticipantCount();
+ break;
+ }
+ }
+
+ private void updateRoomMaxParticipants()
+ {
+ if (room.MaxParticipants == null)
{
slash.FadeOut(transition_duration);
maxText.FadeOut(transition_duration);
@@ -65,9 +87,18 @@ namespace osu.Game.Screens.OnlinePlay.Components
else
{
slash.FadeIn(transition_duration);
- maxText.Text = MaxParticipants.Value.ToString();
+ maxText.Text = room.MaxParticipants.ToString()!;
maxText.FadeIn(transition_duration);
}
}
+
+ private void updateRoomParticipantCount()
+ => count.Text = room.ParticipantCount.ToString("#,0");
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs
index 5128bc4c14..a12d843b0a 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs
@@ -1,25 +1,30 @@
// 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 System.ComponentModel;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class ParticipantsDisplay : OnlinePlayComposite
+ public partial class ParticipantsDisplay : CompositeDrawable
{
- public Bindable Details = new Bindable();
+ public readonly Bindable Details = new Bindable();
- public ParticipantsDisplay(Direction direction)
+ private readonly Room room;
+
+ public ParticipantsDisplay(Room room, Direction direction)
{
+ this.room = room;
OsuScrollContainer scroll;
ParticipantsList list;
AddInternal(scroll = new OsuScrollContainer(direction)
{
- Child = list = new ParticipantsList()
+ Child = list = new ParticipantsList(room)
});
switch (direction)
@@ -46,14 +51,32 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
}
- [BackgroundDependencyLoader]
- private void load()
+ protected override void LoadComplete()
{
- ParticipantCount.BindValueChanged(_ => setParticipantCount());
- MaxParticipants.BindValueChanged(_ => setParticipantCount(), true);
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomParticipantCount();
}
- private void setParticipantCount() =>
- Details.Value = MaxParticipants.Value != null ? $"{ParticipantCount.Value}/{MaxParticipants.Value}" : ParticipantCount.Value.ToString();
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.MaxParticipants):
+ case nameof(Room.ParticipantCount):
+ updateRoomParticipantCount();
+ break;
+ }
+ }
+
+ private void updateRoomParticipantCount()
+ => Details.Value = room.MaxParticipants != null ? $"{room.ParticipantCount}/{room.MaxParticipants}" : room.ParticipantCount.ToString();
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs
index c4aefe4f99..79084a5285 100644
--- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs
@@ -1,21 +1,20 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
-using osu.Framework.Allocation;
+using System.ComponentModel;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading;
using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Online.Rooms;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class ParticipantsList : OnlinePlayComposite
+ public partial class ParticipantsList : CompositeDrawable
{
public const float TILE_SIZE = 35;
@@ -57,15 +56,29 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
}
- [BackgroundDependencyLoader]
- private void load()
+ private readonly Room room;
+
+ public ParticipantsList(Room room)
{
- RecentParticipants.CollectionChanged += (_, _) => updateParticipants();
+ this.room = room;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
updateParticipants();
}
- private ScheduledDelegate scheduledUpdate;
- private FillFlowContainer tiles;
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.RecentParticipants))
+ updateParticipants();
+ }
+
+ private ScheduledDelegate? scheduledUpdate;
+ private FillFlowContainer? tiles;
private void updateParticipants()
{
@@ -83,8 +96,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
Spacing = Vector2.One
};
- for (int i = 0; i < RecentParticipants.Count; i++)
- tiles.Add(new UserTile { User = RecentParticipants[i] });
+ for (int i = 0; i < room.RecentParticipants.Count; i++)
+ tiles.Add(new UserTile { User = room.RecentParticipants[i] });
AddInternal(tiles);
@@ -92,9 +105,15 @@ namespace osu.Game.Screens.OnlinePlay.Components
});
}
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
+
private partial class UserTile : CompositeDrawable
{
- public APIUser User
+ public APIUser? User
{
get => avatar.User;
set => avatar.User = value;
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs
index 0c3b53266c..39b5edbd26 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs
@@ -1,26 +1,28 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class RoomLocalUserInfo : OnlinePlayComposite
+ public partial class RoomLocalUserInfo : CompositeDrawable
{
- private OsuSpriteText attemptDisplay;
+ private readonly Room room;
+ private OsuSpriteText attemptDisplay = null!;
[Resolved]
- private OsuColour colours { get; set; }
+ private OsuColour colours { get; set; } = null!;
- public RoomLocalUserInfo()
+ public RoomLocalUserInfo(Room room)
{
+ this.room = room;
AutoSizeAxes = Axes.Both;
}
@@ -45,19 +47,30 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
base.LoadComplete();
- MaxAttempts.BindValueChanged(_ => updateAttempts());
- UserScore.BindValueChanged(_ => updateAttempts(), true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateAttempts();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.UserScore):
+ case nameof(Room.MaxAttempts):
+ updateAttempts();
+ break;
+ }
}
private void updateAttempts()
{
- if (MaxAttempts.Value != null)
+ if (room.MaxAttempts != null)
{
- attemptDisplay.Text = $"Maximum attempts: {MaxAttempts.Value:N0}";
+ attemptDisplay.Text = $"Maximum attempts: {room.MaxAttempts:N0}";
- if (UserScore.Value != null)
+ if (room.UserScore != null)
{
- int remaining = MaxAttempts.Value.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts);
+ int remaining = room.MaxAttempts.Value - room.UserScore.PlaylistItemAttempts.Sum(a => a.Attempts);
attemptDisplay.Text += $" ({remaining} remaining)";
if (remaining == 0)
@@ -69,5 +82,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
attemptDisplay.Text = string.Empty;
}
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
index cb27d1ee61..ca42e98e3c 100644
--- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs
@@ -1,13 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Development;
@@ -20,18 +17,17 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
public partial class RoomManager : Component, IRoomManager
{
- [CanBeNull]
- public event Action RoomsUpdated;
+ public event Action? RoomsUpdated;
private readonly BindableList rooms = new BindableList();
public IBindableList Rooms => rooms;
- protected IBindable JoinedRoom => joinedRoom;
- private readonly Bindable joinedRoom = new Bindable();
+ protected IBindable JoinedRoom => joinedRoom;
+ private readonly Bindable joinedRoom = new Bindable();
[Resolved]
- private IAPIProvider api { get; set; }
+ private IAPIProvider api { get; set; } = null!;
public RoomManager()
{
@@ -44,9 +40,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
PartRoom();
}
- public virtual void CreateRoom(Room room, Action onSuccess = null, Action onError = null)
+ public virtual void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null)
{
- room.Host.Value = api.LocalUser.Value;
+ room.Host = api.LocalUser.Value;
var req = new CreateRoomRequest(room);
@@ -69,9 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
api.Queue(req);
}
- private JoinRoomRequest currentJoinRoomRequest;
+ private JoinRoomRequest? currentJoinRoomRequest;
- public virtual void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null)
+ public virtual void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
{
currentJoinRoomRequest?.Cancel();
currentJoinRoomRequest = new JoinRoomRequest(room, password);
@@ -97,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
currentJoinRoomRequest?.Cancel();
- if (JoinedRoom.Value == null)
+ if (joinedRoom.Value == null)
return;
if (api.State.Value == APIState.Online)
@@ -111,14 +107,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
public void AddOrUpdateRoom(Room room)
{
Debug.Assert(ThreadSafety.IsUpdateThread);
- Debug.Assert(room.RoomID.Value != null);
+ Debug.Assert(room.RoomID != null);
- if (ignoredRooms.Contains(room.RoomID.Value.Value))
+ if (ignoredRooms.Contains(room.RoomID.Value))
return;
try
{
- var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
+ var existing = rooms.FirstOrDefault(e => e.RoomID == room.RoomID);
if (existing == null)
rooms.Add(room);
else
@@ -126,9 +122,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
catch (Exception ex)
{
- Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
+ Logger.Error(ex, $"Failed to update room: {room.Name}.");
- ignoredRooms.Add(room.RoomID.Value.Value);
+ ignoredRooms.Add(room.RoomID.Value);
rooms.Remove(room);
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
index 780ee29e41..8b80228ae1 100644
--- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.Threading.Tasks;
using osu.Game.Online.Rooms;
@@ -20,21 +18,21 @@ namespace osu.Game.Screens.OnlinePlay.Components
this.room = room;
}
- private GetRoomRequest lastPollRequest;
+ private GetRoomRequest? lastPollRequest;
protected override Task Poll()
{
if (!API.IsLoggedIn)
return base.Poll();
- if (room.RoomID.Value == null)
+ if (room.RoomID == null)
return base.Poll();
var tcs = new TaskCompletionSource();
lastPollRequest?.Cancel();
- var req = new GetRoomRequest(room.RoomID.Value.Value);
+ var req = new GetRoomRequest(room.RoomID.Value);
req.Success += result =>
{
diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
index 2ee3bb30dd..2bdb41ce12 100644
--- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.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.
-#nullable disable
-
using System;
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -13,22 +12,27 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
+using osu.Game.Online.Rooms;
using osuTK;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Components
{
- public partial class StarRatingRangeDisplay : OnlinePlayComposite
+ public partial class StarRatingRangeDisplay : CompositeDrawable
{
+ private readonly Room room;
+
[Resolved]
- private OsuColour colours { get; set; }
+ private OsuColour colours { get; set; } = null!;
- private StarRatingDisplay minDisplay;
- private Drawable minBackground;
- private StarRatingDisplay maxDisplay;
- private Drawable maxBackground;
+ private StarRatingDisplay minDisplay = null!;
+ private Drawable minBackground = null!;
+ private StarRatingDisplay maxDisplay = null!;
+ private Drawable maxBackground = null!;
- public StarRatingRangeDisplay()
+ public StarRatingRangeDisplay(Room room)
{
+ this.room = room;
AutoSizeAxes = Axes.Both;
}
@@ -76,8 +80,19 @@ namespace osu.Game.Screens.OnlinePlay.Components
{
base.LoadComplete();
- DifficultyRange.BindValueChanged(_ => updateRange());
- Playlist.BindCollectionChanged((_, _) => updateRange(), true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRange();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.Playlist):
+ case nameof(Room.DifficultyRange):
+ updateRange();
+ break;
+ }
}
private void updateRange()
@@ -85,16 +100,16 @@ namespace osu.Game.Screens.OnlinePlay.Components
StarDifficulty minDifficulty;
StarDifficulty maxDifficulty;
- if (DifficultyRange.Value != null && Playlist.Count == 0)
+ if (room.DifficultyRange != null && room.Playlist.Count == 0)
{
// When Playlist is empty (in lounge) we take retrieved range
- minDifficulty = new StarDifficulty(DifficultyRange.Value.Min, 0);
- maxDifficulty = new StarDifficulty(DifficultyRange.Value.Max, 0);
+ minDifficulty = new StarDifficulty(room.DifficultyRange.Min, 0);
+ maxDifficulty = new StarDifficulty(room.DifficultyRange.Max, 0);
}
else
{
// When Playlist is not empty (in room) we compute actual range
- var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
+ var orderedDifficulties = room.Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
@@ -107,5 +122,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
minBackground.Colour = colours.ForStarDifficulty(minDifficulty.Stars);
maxBackground.Colour = colours.ForStarDifficulty(maxDifficulty.Stars);
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs
index ed39021a73..2b1233506f 100644
--- a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs
@@ -1,39 +1,52 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using System.ComponentModel;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Online.Rooms;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Components
{
public partial class StatusColouredContainer : Container
{
+ [Resolved]
+ private OsuColour colours { get; set; } = null!;
+
private readonly double transitionDuration;
+ private readonly Room room;
- [Resolved(typeof(Room), nameof(Room.Status))]
- private Bindable status { get; set; }
-
- [Resolved(typeof(Room), nameof(Room.Category))]
- private Bindable category { get; set; }
-
- public StatusColouredContainer(double transitionDuration = 100)
+ public StatusColouredContainer(Room room, double transitionDuration = 100)
{
+ this.room = room;
this.transitionDuration = transitionDuration;
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
+ protected override void LoadComplete()
{
- status.BindValueChanged(s =>
- {
- this.FadeColour(colours.ForRoomCategory(category.Value) ?? s.NewValue.GetAppropriateColour(colours), transitionDuration);
- }, true);
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomStatus();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Status))
+ updateRoomStatus();
+ }
+
+ private void updateRoomStatus()
+ {
+ this.FadeColour(colours.ForRoomCategory(room.Category) ?? room.Status.GetAppropriateColour(colours), transitionDuration);
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
index 1aaf0a4321..0dc7e7930a 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs
@@ -119,14 +119,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
}
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
- {
- return new CachedModelDependencyContainer(base.CreateChildDependencies(parent))
- {
- Model = { Value = room }
- };
- }
-
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
@@ -228,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
Origin = Anchor.Centre,
Children = new Drawable[]
{
- new DailyChallengeTimeRemainingRing(),
+ new DailyChallengeTimeRemainingRing(room),
breakdown = new DailyChallengeScoreBreakdown(),
totals = new DailyChallengeTotalsDisplay(),
}
@@ -301,7 +293,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
Spacing = new Vector2(10),
Children = new Drawable[]
{
- new PlaylistsReadyButton
+ new PlaylistsReadyButton(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -353,12 +345,12 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
private void presentScore(long id)
{
if (this.IsCurrentScreen())
- this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id));
+ this.Push(new PlaylistItemScoreResultsScreen(room.RoomID!.Value, playlistItem, id));
}
private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e)
{
- if (e.RoomID != room.RoomID.Value || e.PlaylistItemID != playlistItem.ID)
+ if (e.RoomID != room.RoomID || e.PlaylistItemID != playlistItem.ID)
return;
userLookupCache.GetUserAsync(e.UserID).ContinueWith(t =>
@@ -410,7 +402,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
private void dailyChallengeChanged(ValueChangedEvent change)
{
- if (change.OldValue?.RoomID == room.RoomID.Value && change.NewValue == null && metadataClient.IsConnected.Value)
+ if (change.OldValue?.RoomID == room.RoomID && change.NewValue == null && metadataClient.IsConnected.Value)
{
notificationOverlay?.Post(new SimpleNotification { Text = DailyChallengeStrings.ChallengeEndedNotification });
}
@@ -437,7 +429,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
roomManager.JoinRoom(room);
startLoopingTrack(this, musicController);
- metadataClient.BeginWatchingMultiplayerRoom(room.RoomID.Value!.Value).ContinueWith(t =>
+ metadataClient.BeginWatchingMultiplayerRoom(room.RoomID!.Value).ContinueWith(t =>
{
if (t.Exception != null)
{
@@ -489,7 +481,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
roomManager.PartRoom();
- metadataClient.EndWatchingMultiplayerRoom(room.RoomID.Value!.Value).FireAndForget();
+ metadataClient.EndWatchingMultiplayerRoom(room.RoomID!.Value).FireAndForget();
return base.OnExiting(e);
}
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs
index 7f0f26097c..7fddb8d1c4 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs
@@ -169,7 +169,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = room.Name.Value.Split(':', StringSplitOptions.TrimEntries).Last(),
+ Text = room.Name.Split(':', StringSplitOptions.TrimEntries).Last(),
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f },
Shear = new Vector2(-OsuGame.SHEAR, 0f),
Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate),
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs
index c9152393e7..9fe2b70a5a 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs
@@ -138,7 +138,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
if (request?.CompletionState == APIRequestCompletionState.Waiting)
return;
- request = new IndexPlaylistScoresRequest(room.RoomID.Value!.Value, playlistItem.ID);
+ request = new IndexPlaylistScoresRequest(room.RoomID!.Value, playlistItem.ID);
request.Success += req => Schedule(() =>
{
diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs
index e86f26ad6b..bf01ee6b52 100644
--- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs
+++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -10,15 +11,15 @@ using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{
- public partial class DailyChallengeTimeRemainingRing : OnlinePlayComposite
+ public partial class DailyChallengeTimeRemainingRing : CompositeDrawable
{
- private CircularProgress progress = null!;
- private OsuSpriteText timeText = null!;
+ private readonly Room room;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
@@ -26,6 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
[Resolved]
private OsuColour colours { get; set; } = null!;
+ private CircularProgress progress = null!;
+ private OsuSpriteText timeText = null!;
+
+ public DailyChallengeTimeRemainingRing(Room room)
+ {
+ this.room = room;
+ }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -90,12 +99,23 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{
base.LoadComplete();
- StartDate.BindValueChanged(_ => Scheduler.AddOnce(updateState));
- EndDate.BindValueChanged(_ => Scheduler.AddOnce(updateState));
+ room.PropertyChanged += onRoomPropertyChanged;
updateState();
+
FinishTransforms(true);
}
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.StartDate):
+ case nameof(Room.EndDate):
+ Scheduler.AddOnce(updateState);
+ break;
+ }
+ }
+
private ScheduledDelegate? scheduledUpdate;
private void updateState()
@@ -105,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
const float transition_duration = 300;
- if (StartDate.Value == null || EndDate.Value == null || EndDate.Value < DateTimeOffset.Now)
+ if (room.StartDate == null || room.EndDate == null || room.EndDate < DateTimeOffset.Now)
{
timeText.Text = TimeSpan.Zero.ToString(@"hh\:mm\:ss");
progress.Progress = 0;
@@ -114,8 +134,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
return;
}
- var roomDuration = EndDate.Value.Value - StartDate.Value.Value;
- var remaining = EndDate.Value.Value - DateTimeOffset.Now;
+ var roomDuration = room.EndDate.Value - room.StartDate.Value;
+ var remaining = room.EndDate.Value - DateTimeOffset.Now;
timeText.Text = remaining.ToString(@"hh\:mm\:ss");
progress.Progress = remaining.TotalSeconds / roomDuration.TotalSeconds;
@@ -138,5 +158,11 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
scheduledUpdate = Scheduler.AddDelayed(updateState, 1000);
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs
index ef06d21655..c39ca347c7 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.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.
-#nullable disable
-
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -16,6 +16,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@@ -26,30 +27,33 @@ using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Components;
using osuTK;
using osuTK.Graphics;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
- public partial class DrawableRoom : CompositeDrawable
+ public abstract partial class DrawableRoom : CompositeDrawable
{
protected const float CORNER_RADIUS = 10;
private const float height = 100;
public readonly Room Room;
- protected Container ButtonsContainer { get; private set; }
+ protected readonly Bindable SelectedItem = new Bindable();
+ protected Container ButtonsContainer { get; private set; } = null!;
private readonly Bindable roomType = new Bindable();
private readonly Bindable roomCategory = new Bindable();
private readonly Bindable hasPassword = new Bindable();
- private DrawableRoomParticipantsList drawableRoomParticipantsList;
- private RoomSpecialCategoryPill specialCategoryPill;
- private PasswordProtectedIcon passwordIcon;
- private EndDateInfo endDateInfo;
+ private DrawableRoomParticipantsList? drawableRoomParticipantsList;
+ private RoomSpecialCategoryPill? specialCategoryPill;
+ private PasswordProtectedIcon? passwordIcon;
+ private EndDateInfo? endDateInfo;
+ private SpriteText? roomName;
+ private UpdateableBeatmapBackgroundSprite background = null!;
+ private DelayedLoadWrapper wrapper = null!;
- private DelayedLoadWrapper wrapper;
-
- public DrawableRoom(Room room)
+ protected DrawableRoom(Room room)
{
Room = room;
@@ -77,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
AutoSizeAxes = Axes.X
};
- InternalChildren = new[]
+ InternalChildren = new Drawable[]
{
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
new Box
@@ -85,7 +89,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
RelativeSizeAxes = Axes.Both,
Colour = colours.Background5,
},
- CreateBackground().With(d =>
+ background = CreateBackground().With(d =>
{
d.RelativeSizeAxes = Axes.Both;
}),
@@ -155,17 +159,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Spacing = new Vector2(5),
Children = new Drawable[]
{
- new RoomStatusPill
+ new RoomStatusPill(Room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
- specialCategoryPill = new RoomSpecialCategoryPill
+ specialCategoryPill = new RoomSpecialCategoryPill(Room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
- endDateInfo = new EndDateInfo
+ endDateInfo = new EndDateInfo(Room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -180,13 +184,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new TruncatingSpriteText
+ roomName = new TruncatingSpriteText
{
RelativeSizeAxes = Axes.X,
- Font = OsuFont.GetFont(size: 28),
- Current = { BindTarget = Room.Name }
+ Font = OsuFont.GetFont(size: 28)
},
- new RoomStatusText()
+ new RoomStatusText(Room)
+ {
+ SelectedItem = { BindTarget = SelectedItem }
+ }
}
}
},
@@ -218,7 +224,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Children = new Drawable[]
{
ButtonsContainer,
- drawableRoomParticipantsList = new DrawableRoomParticipantsList
+ drawableRoomParticipantsList = new DrawableRoomParticipantsList(Room)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
@@ -243,36 +249,71 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
base.LoadComplete();
+ Room.PropertyChanged += onRoomPropertyChanged;
+
wrapper.DelayedLoadComplete += _ =>
{
+ Debug.Assert(specialCategoryPill != null);
+ Debug.Assert(endDateInfo != null);
+ Debug.Assert(passwordIcon != null);
+
wrapper.FadeInFromZero(200);
- roomCategory.BindTo(Room.Category);
- roomCategory.BindValueChanged(c =>
- {
- if (c.NewValue > RoomCategory.Normal)
- specialCategoryPill.Show();
- else
- specialCategoryPill.Hide();
- }, true);
-
- roomType.BindTo(Room.Type);
- roomType.BindValueChanged(t =>
- {
- endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0;
- }, true);
-
- hasPassword.BindTo(Room.HasPassword);
- hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true);
+ updateRoomName();
+ updateRoomCategory();
+ updateRoomType();
+ updateRoomHasPassword();
};
+
+ SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
}
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
- return new CachedModelDependencyContainer(base.CreateChildDependencies(parent))
+ switch (e.PropertyName)
{
- Model = { Value = Room }
- };
+ case nameof(Room.Name):
+ updateRoomName();
+ break;
+
+ case nameof(Room.Category):
+ updateRoomCategory();
+ break;
+
+ case nameof(Room.Type):
+ updateRoomType();
+ break;
+
+ case nameof(Room.HasPassword):
+ updateRoomHasPassword();
+ break;
+ }
+ }
+
+ private void updateRoomName()
+ {
+ if (roomName != null)
+ roomName.Text = Room.Name;
+ }
+
+ private void updateRoomCategory()
+ {
+ if (Room.Category > RoomCategory.Normal)
+ specialCategoryPill?.Show();
+ else
+ specialCategoryPill?.Hide();
+ }
+
+ private void updateRoomType()
+ {
+ if (endDateInfo != null)
+ endDateInfo.Alpha = Room.Type == MatchType.Playlists ? 1 : 0;
+ }
+
+ private void updateRoomHasPassword()
+ {
+ if (passwordIcon != null)
+ passwordIcon.Alpha = Room.HasPassword ? 1 : 0;
}
private int numberOfAvatars = 7;
@@ -289,29 +330,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
}
- protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite();
+ protected virtual UpdateableBeatmapBackgroundSprite CreateBackground() => new UpdateableBeatmapBackgroundSprite();
protected virtual IEnumerable CreateBottomDetails()
{
var pills = new List();
- if (Room.Type.Value != MatchType.Playlists)
+ if (Room.Type != MatchType.Playlists)
{
- pills.AddRange(new OnlinePlayComposite[]
+ pills.AddRange(new Drawable[]
{
- new MatchTypePill(),
- new QueueModePill(),
+ new MatchTypePill(Room),
+ new QueueModePill(Room),
});
}
pills.AddRange(new Drawable[]
{
- new PlaylistCountPill
+ new PlaylistCountPill(Room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
- new StarRatingRangeDisplay
+ new StarRatingRangeDisplay(Room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -322,19 +363,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
return pills;
}
- private partial class RoomStatusText : OnlinePlayComposite
+ protected override void Dispose(bool isDisposing)
{
- [Resolved]
- private OsuColour colours { get; set; }
+ base.Dispose(isDisposing);
+ Room.PropertyChanged -= onRoomPropertyChanged;
+ }
+
+ private partial class RoomStatusText : CompositeDrawable
+ {
+ public readonly IBindable SelectedItem = new Bindable();
[Resolved]
- private BeatmapLookupCache beatmapLookupCache { get; set; }
+ private OsuColour colours { get; set; } = null!;
- private SpriteText statusText;
- private LinkFlowContainer beatmapText;
+ [Resolved]
+ private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
- public RoomStatusText()
+ private readonly Room room;
+ private SpriteText statusText = null!;
+ private LinkFlowContainer beatmapText = null!;
+
+ public RoomStatusText(Room room)
{
+ this.room = room;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
@@ -383,17 +434,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
protected override void LoadComplete()
{
base.LoadComplete();
- CurrentPlaylistItem.BindValueChanged(onSelectedItemChanged, true);
+ SelectedItem.BindValueChanged(onSelectedItemChanged, true);
}
- private CancellationTokenSource beatmapLookupCancellation;
+ private CancellationTokenSource? beatmapLookupCancellation;
- private void onSelectedItemChanged(ValueChangedEvent item)
+ private void onSelectedItemChanged(ValueChangedEvent item)
{
beatmapLookupCancellation?.Cancel();
beatmapText.Clear();
- if (Type.Value == MatchType.Playlists)
+ if (room.Type == MatchType.Playlists)
{
statusText.Text = "Ready to play";
return;
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs
index 60e05285d9..5bcc974c26 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs
@@ -1,13 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
-using System.Collections.Specialized;
-using System.Diagnostics;
+using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -16,31 +13,33 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Users.Drawables;
using osuTK;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
- public partial class DrawableRoomParticipantsList : OnlinePlayComposite
+ public partial class DrawableRoomParticipantsList : CompositeDrawable
{
public const float SHEAR_WIDTH = 12f;
-
private const float avatar_size = 36;
-
private const float height = 60f;
-
private static readonly Vector2 shear = new Vector2(SHEAR_WIDTH / height, 0);
- private FillFlowContainer avatarFlow;
+ private readonly Room room;
- private CircularAvatar hostAvatar;
- private LinkFlowContainer hostText;
- private HiddenUserCount hiddenUsers;
- private OsuSpriteText totalCount;
+ private FillFlowContainer avatarFlow = null!;
+ private CircularAvatar hostAvatar = null!;
+ private LinkFlowContainer hostText = null!;
+ private HiddenUserCount hiddenUsers = null!;
+ private OsuSpriteText totalCount = null!;
- public DrawableRoomParticipantsList()
+ public DrawableRoomParticipantsList(Room room)
{
+ this.room = room;
+
AutoSizeAxes = Axes.X;
Height = height;
}
@@ -165,14 +164,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
base.LoadComplete();
- RecentParticipants.BindCollectionChanged(onParticipantsChanged, true);
- ParticipantCount.BindValueChanged(_ =>
- {
- updateHiddenUsers();
- totalCount.Text = ParticipantCount.Value.ToString();
- }, true);
+ room.PropertyChanged += onRoomPropertyChanged;
- Host.BindValueChanged(onHostChanged, true);
+ updateRoomHost();
+ updateRoomParticipantCount();
+ updateRoomParticipants();
}
private int numberOfCircles = 4;
@@ -192,43 +188,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
// Reinitialising the list looks janky, but this is unlikely to be used in a setting where it's visible.
clearUsers();
- foreach (var u in RecentParticipants)
+ foreach (var u in room.RecentParticipants)
addUser(u);
updateHiddenUsers();
}
}
- private void onParticipantsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void updateRoomParticipants()
{
- switch (e.Action)
+ HashSet newUsers = room.RecentParticipants.ToHashSet();
+
+ avatarFlow.RemoveAll(a =>
{
- case NotifyCollectionChangedAction.Add:
- Debug.Assert(e.NewItems != null);
+ // Avatar with no user. Really shouldn't ever be the case but asserting it correctly is difficult.
+ if (a.User == null)
+ return false;
- foreach (var added in e.NewItems.OfType())
- addUser(added);
- break;
+ // User was previously and still is a participant. Keep them around but remove them from the new set.
+ // This will be useful when we add all remaining users (now just the new participants) to the flow.
+ if (newUsers.Contains(a.User))
+ {
+ newUsers.Remove(a.User);
+ return false;
+ }
- case NotifyCollectionChangedAction.Remove:
- Debug.Assert(e.OldItems != null);
+ // User is no longer a participant. Remove them from the flow.
+ return true;
+ }, true);
- foreach (var removed in e.OldItems.OfType())
- removeUser(removed);
- break;
-
- case NotifyCollectionChangedAction.Reset:
- clearUsers();
- break;
-
- case NotifyCollectionChangedAction.Replace:
- case NotifyCollectionChangedAction.Move:
- // Easiest is to just reinitialise the whole list. These are unlikely to ever be use cases.
- clearUsers();
- foreach (var u in RecentParticipants)
- addUser(u);
- break;
- }
+ // Add all remaining users to the flow.
+ foreach (var u in newUsers)
+ addUser(u);
updateHiddenUsers();
}
@@ -241,11 +232,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
avatarFlow.Add(new CircularAvatar { User = user });
}
- private void removeUser(APIUser user)
- {
- avatarFlow.RemoveAll(a => a.User == user, true);
- }
-
private void clearUsers()
{
avatarFlow.Clear();
@@ -255,8 +241,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void updateHiddenUsers()
{
int hiddenCount = 0;
- if (RecentParticipants.Count > NumberOfCircles)
- hiddenCount = ParticipantCount.Value - NumberOfCircles + 1;
+ if (room.RecentParticipants.Count > NumberOfCircles)
+ hiddenCount = room.ParticipantCount - NumberOfCircles + 1;
hiddenUsers.Count = hiddenCount;
@@ -264,26 +250,56 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
avatarFlow.Remove(avatarFlow.Last(), true);
else if (displayedCircles < NumberOfCircles)
{
- var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
+ var nextUser = room.RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
if (nextUser != null) addUser(nextUser);
}
}
- private void onHostChanged(ValueChangedEvent host)
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
- hostAvatar.User = host.NewValue;
+ switch (e.PropertyName)
+ {
+ case nameof(Room.Host):
+ updateRoomHost();
+ break;
+
+ case nameof(Room.ParticipantCount):
+ updateRoomParticipantCount();
+ break;
+
+ case nameof(Room.RecentParticipants):
+ updateRoomParticipants();
+ break;
+ }
+ }
+
+ private void updateRoomHost()
+ {
+ hostAvatar.User = room.Host;
hostText.Clear();
- if (host.NewValue != null)
+ if (room.Host != null)
{
hostText.AddText("hosted by ");
- hostText.AddUserLink(host.NewValue);
+ hostText.AddUserLink(room.Host);
}
}
+ private void updateRoomParticipantCount()
+ {
+ updateHiddenUsers();
+ totalCount.Text = room.ParticipantCount.ToString();
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
+
private partial class CircularAvatar : CompositeDrawable
{
- public APIUser User
+ public APIUser? User
{
get => avatar.User;
set => avatar.User = value;
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs
index 844991095e..3b03ce61f1 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs
@@ -2,49 +2,69 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.ComponentModel;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
- public partial class EndDateInfo : OnlinePlayComposite
+ public partial class EndDateInfo : CompositeDrawable
{
- public EndDateInfo()
+ private readonly Room room;
+
+ public EndDateInfo(Room room)
{
+ this.room = room;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = new EndDatePart
+ InternalChild = new EndDatePart(room)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12),
- EndDate = { BindTarget = EndDate }
+ Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12)
};
}
private partial class EndDatePart : DrawableDate
{
- public readonly IBindable EndDate = new Bindable();
+ private readonly Room room;
- public EndDatePart()
+ public EndDatePart(Room room)
: base(DateTimeOffset.UtcNow)
{
- EndDate.BindValueChanged(date =>
- {
- // If null, set a very large future date to prevent unnecessary schedules.
- Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1);
- }, true);
+ this.room = room;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateEndDate();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.EndDate))
+ updateEndDate();
+ }
+
+ private void updateEndDate()
+ {
+ // If null, set a very large future date to prevent unnecessary schedules.
+ Date = room.EndDate ?? DateTimeOffset.Now.AddYears(1);
}
protected override string Format()
{
- if (EndDate.Value == null)
+ if (room.EndDate == null)
return string.Empty;
var diffToNow = Date.Subtract(DateTimeOffset.Now);
@@ -60,6 +80,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
return $"Closing {base.Format()}";
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs
index e30d673b26..d5405c2d0e 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs
@@ -1,7 +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 osu.Framework.Bindables;
+using System.ComponentModel;
using osu.Framework.Extensions;
using osu.Game.Online.Rooms;
@@ -9,16 +9,36 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class MatchTypePill : OnlinePlayPill
{
+ private readonly Room room;
+
+ public MatchTypePill(Room room)
+ {
+ this.room = room;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
- Type.BindValueChanged(onMatchTypeChanged, true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomType();
}
- private void onMatchTypeChanged(ValueChangedEvent type)
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
- TextFlow.Text = type.NewValue.GetLocalisableDescription();
+ if (e.PropertyName == nameof(Room.Type))
+ updateRoomType();
+ }
+
+ private void updateRoomType()
+ {
+ TextFlow.Text = room.Type.GetLocalisableDescription();
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs
index 3e6d7a2e54..c65a5e2469 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs
@@ -3,13 +3,14 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
- public abstract partial class OnlinePlayPill : OnlinePlayComposite
+ public abstract partial class OnlinePlayPill : CompositeDrawable
{
protected PillContainer Pill { get; private set; } = null!;
protected OsuTextFlowContainer TextFlow { get; private set; } = null!;
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs
index fe5ccb4f09..70ddf15abf 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs
@@ -1,10 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.ComponentModel;
using System.Linq;
using Humanizer;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Game.Graphics;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
@@ -13,26 +15,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
///
public partial class PlaylistCountPill : OnlinePlayPill
{
+ private readonly Room room;
+
+ public PlaylistCountPill(Room room)
+ {
+ this.room = room;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
- PlaylistItemStats.BindValueChanged(_ => updateCount());
- Playlist.BindCollectionChanged((_, _) => updateCount(), true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateCount();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.Playlist):
+ case nameof(Room.PlaylistItemStats):
+ updateCount();
+ break;
+ }
}
private void updateCount()
{
- int activeItems = Playlist.Count > 0 || PlaylistItemStats.Value == null
+ int activeItems = room.Playlist.Count > 0 || room.PlaylistItemStats == null
// For now, use the playlist as the source of truth if it has any items.
// This allows the count to display correctly on the room screen (after joining a room).
- ? Playlist.Count(i => !i.Expired)
- : PlaylistItemStats.Value.CountActive;
+ ? room.Playlist.Count(i => !i.Expired)
+ : room.PlaylistItemStats.CountActive;
TextFlow.Clear();
TextFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold));
TextFlow.AddText(" ");
TextFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None));
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs
index 23f4ecf8db..c7d7876644 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs
@@ -1,24 +1,42 @@
// 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 System.ComponentModel;
using osu.Framework.Extensions;
-using osu.Game.Online.Multiplayer;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class QueueModePill : OnlinePlayPill
{
+ private readonly Room room;
+
+ public QueueModePill(Room room)
+ {
+ this.room = room;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
- QueueMode.BindValueChanged(onQueueModeChanged, true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomQueueMode();
}
- private void onQueueModeChanged(ValueChangedEvent mode)
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
- TextFlow.Text = mode.NewValue.GetLocalisableDescription();
+ if (e.PropertyName == nameof(Room.QueueMode))
+ updateRoomQueueMode();
+ }
+
+ private void updateRoomQueueMode()
+ => TextFlow.Text = room.QueueMode.GetLocalisableDescription();
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs
index 9b8954bb33..9bb3a59d0c 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs
@@ -1,21 +1,30 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
+using osu.Game.Online.Rooms;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class RoomSpecialCategoryPill : OnlinePlayPill
{
+ private readonly Room room;
+
[Resolved]
private OsuColour colours { get; set; } = null!;
protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold);
+ public RoomSpecialCategoryPill(Room room)
+ {
+ this.room = room;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
@@ -23,11 +32,26 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Pill.Background.Alpha = 1;
TextFlow.Colour = Color4.Black;
- Category.BindValueChanged(c =>
- {
- TextFlow.Text = c.NewValue.GetLocalisableDescription();
- Pill.Background.Colour = colours.ForRoomCategory(c.NewValue) ?? colours.Pink;
- }, true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomCategory();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Category))
+ updateRoomCategory();
+ }
+
+ private void updateRoomCategory()
+ {
+ TextFlow.Text = room.Category.GetLocalisableDescription();
+ Pill.Background.Colour = colours.ForRoomCategory(room.Category) ?? colours.Pink;
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs
index 96d698a184..b3dc617fd6 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.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.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
@@ -19,25 +20,47 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold);
+ private readonly Room room;
+
+ public RoomStatusPill(Room room)
+ {
+ this.room = room;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
- EndDate.BindValueChanged(_ => updateDisplay());
- Status.BindValueChanged(_ => updateDisplay(), true);
-
- FinishTransforms(true);
-
TextFlow.Colour = Colour4.Black;
Pill.Background.Alpha = 1;
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateDisplay();
+
+ FinishTransforms(true);
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.Status):
+ case nameof(Room.EndDate):
+ updateDisplay();
+ break;
+ }
}
private void updateDisplay()
{
- RoomStatus status = Status.Value;
+ Pill.Background.FadeColour(room.Status.GetAppropriateColour(colours), 100);
+ TextFlow.Text = room.Status.Message;
+ }
- Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100);
- TextFlow.Text = status.Message;
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index e842f8c436..17aed021b2 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -11,6 +9,7 @@ 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,8 +23,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public partial class RoomsContainer : CompositeDrawable, IKeyBindingHandler
{
- public readonly Bindable SelectedRoom = new Bindable();
- public readonly Bindable Filter = new Bindable();
+ public readonly Bindable SelectedRoom = new Bindable();
+ public readonly Bindable Filter = new Bindable();
public IReadOnlyList Rooms => roomFlow.FlowingChildren.Cast().ToArray();
@@ -33,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private readonly FillFlowContainer roomFlow;
[Resolved]
- private IRoomManager roomManager { get; set; }
+ private IRoomManager roomManager { get; set; } = null!;
// handle deselection
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
@@ -67,10 +66,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
rooms.BindTo(roomManager.Rooms);
- Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
+ Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
}
- private void applyFilterCriteria(FilterCriteria criteria)
+ private void applyFilterCriteria(FilterCriteria? criteria)
{
roomFlow.Children.ForEach(r =>
{
@@ -80,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
bool matchingFilter = true;
- matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats.Value?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
+ matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
if (!string.IsNullOrEmpty(criteria.SearchString))
{
@@ -102,10 +101,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
return true;
case RoomPermissionsFilter.Public:
- return !room.Room.HasPassword.Value;
+ return !room.Room.HasPassword;
case RoomPermissionsFilter.Private:
- return room.Room.HasPassword.Value;
+ return room.Room.HasPassword;
default:
throw new ArgumentOutOfRangeException(nameof(accessType), accessType, $"Unsupported {nameof(RoomPermissionsFilter)} in filter");
@@ -113,7 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
}
- private void roomsChanged(object sender, NotifyCollectionChangedEventArgs args)
+ private void roomsChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
@@ -140,9 +139,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void addRooms(IEnumerable rooms)
{
foreach (var room in rooms)
- roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } });
+ roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = SelectedRoom });
- applyFilterCriteria(Filter?.Value);
+ applyFilterCriteria(Filter.Value);
}
private void removeRooms(IEnumerable rooms)
@@ -170,10 +169,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
foreach (var room in roomFlow)
{
- roomFlow.SetLayoutPosition(room, room.Room.Category.Value > RoomCategory.Normal
+ roomFlow.SetLayoutPosition(room, room.Room.Category > RoomCategory.Normal
// Always show spotlight playlists at the top of the listing.
? float.MinValue
- : -(room.Room.RoomID.Value ?? 0));
+ : -(room.Room.RoomID ?? 0));
}
}
@@ -213,7 +212,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
- Room room;
+ Room? room;
if (SelectedRoom.Value == null)
room = visibleRooms.FirstOrDefault()?.Room;
@@ -236,7 +235,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
base.Dispose(isDisposing);
- if (roomManager != null)
+ if (roomManager.IsNotNull())
roomManager.RoomsUpdated -= updateSorting;
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
index fed47e847a..d396d18b4f 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.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.
-#nullable disable
-
using System.Collections.Generic;
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -28,6 +27,7 @@ using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osuTK;
using osuTK.Graphics;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Lounge
{
@@ -39,14 +39,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private const float transition_duration = 60;
private const float selection_border_width = 4;
- public readonly Bindable SelectedRoom = new Bindable();
+ public required Bindable SelectedRoom
+ {
+ get => selectedRoom;
+ set => selectedRoom.Current = value;
+ }
[Resolved(canBeNull: true)]
- private LoungeSubScreen lounge { get; set; }
+ private LoungeSubScreen? lounge { get; set; }
- private Sample sampleSelect;
- private Sample sampleJoin;
- private Drawable selectionBox;
+ private readonly BindableWithCurrent selectedRoom = new BindableWithCurrent();
+ private Sample? sampleSelect;
+ private Sample? sampleJoin;
+ private Drawable selectionBox = null!;
public DrawableLoungeRoom(Room room)
: base(room)
@@ -61,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AddRangeInternal(new Drawable[]
{
- new StatusColouredContainer(transition_duration)
+ new StatusColouredContainer(Room, transition_duration)
{
RelativeSizeAxes = Axes.Both,
Child = selectionBox = new Container
@@ -89,12 +94,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
base.LoadComplete();
Alpha = matchingFilter ? 1 : 0;
- selectionBox.Alpha = SelectedRoom.Value == Room ? 1 : 0;
+ selectionBox.Alpha = selectedRoom.Value == Room ? 1 : 0;
- SelectedRoom.BindValueChanged(updateSelectedRoom);
+ selectedRoom.BindValueChanged(updateSelectedRoom);
+
+ Room.PropertyChanged += onRoomPropertyChanged;
+ updateSelectedItem();
}
- private void updateSelectedRoom(ValueChangedEvent selected)
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.CurrentPlaylistItem))
+ updateSelectedItem();
+ }
+
+ private void updateSelectedItem()
+ => SelectedItem.Value = Room.CurrentPlaylistItem;
+
+ private void updateSelectedRoom(ValueChangedEvent selected)
{
if (selected.NewValue == Room)
selectionBox.FadeIn(transition_duration);
@@ -104,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public bool FilteringActive { get; set; }
- public IEnumerable FilterTerms => new LocalisableString[] { Room.Name.Value };
+ public IEnumerable FilterTerms => new LocalisableString[] { Room.Name };
private bool matchingFilter = true;
@@ -140,7 +157,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
if (e.Repeat)
return false;
- if (SelectedRoom.Value != Room)
+ if (selectedRoom.Value != Room)
return false;
switch (e.Action)
@@ -157,18 +174,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
}
- protected override bool ShouldBeConsideredForInput(Drawable child) => SelectedRoom.Value == Room || child is HoverSounds;
+ protected override bool ShouldBeConsideredForInput(Drawable child) => selectedRoom.Value == Room || child is HoverSounds;
protected override bool OnClick(ClickEvent e)
{
- if (Room != SelectedRoom.Value)
+ if (Room != selectedRoom.Value)
{
sampleSelect?.Play();
- SelectedRoom.Value = Room;
+ selectedRoom.Value = Room;
return true;
}
- if (Room.HasPassword.Value)
+ if (Room.HasPassword)
{
this.ShowPopover();
return true;
@@ -179,12 +196,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
return true;
}
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ Room.PropertyChanged -= onRoomPropertyChanged;
+ }
+
public partial class PasswordEntryPopover : OsuPopover
{
private readonly Room room;
[Resolved(canBeNull: true)]
- private LoungeSubScreen lounge { get; set; }
+ private LoungeSubScreen? lounge { get; set; }
public override bool HandleNonPositionalInput => true;
@@ -195,10 +218,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
this.room = room;
}
- private OsuPasswordTextBox passwordTextBox;
- private RoundedButton joinButton;
- private OsuSpriteText errorText;
- private Sample sampleJoinFail;
+ private OsuPasswordTextBox passwordTextBox = null!;
+ private RoundedButton joinButton = null!;
+ private OsuSpriteText errorText = null!;
+ private Sample? sampleJoinFail;
[BackgroundDependencyLoader]
private void load(OsuColour colours, AudioManager audio)
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs
index b31c351b82..4a3985c386 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.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.ComponentModel;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Game.Online.Rooms;
@@ -19,21 +20,44 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
playlist.BindCollectionChanged((_, _) => PlaylistItem = playlist.GetCurrentItem());
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ SelectedRoom.BindValueChanged(onSelectedRoomChanged, true);
+ }
+
private void onSelectedRoomChanged(ValueChangedEvent room)
{
if (room.OldValue != null)
- playlist.UnbindFrom(room.OldValue.Playlist);
+ room.OldValue.PropertyChanged -= onRoomPropertyChanged;
if (room.NewValue != null)
- playlist.BindTo(room.NewValue.Playlist);
- else
- playlist.Clear();
+ room.NewValue.PropertyChanged += onRoomPropertyChanged;
+
+ updateCurrentItem();
}
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ updateCurrentItem();
+ }
+
+ private void updateCurrentItem()
+ => PlaylistItem = SelectedRoom.Value?.Playlist.GetCurrentItem();
+
public override bool OnExiting(ScreenExitEvent e)
{
// This screen never exits.
return true;
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (SelectedRoom.Value != null)
+ SelectedRoom.Value.PropertyChanged -= onRoomPropertyChanged;
+ }
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
index 3792a67896..90288a1067 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
@@ -259,7 +259,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
selectionLease.Return();
selectionLease = null;
- if (SelectedRoom.Value?.RoomID.Value == null)
+ if (SelectedRoom.Value?.RoomID == null)
SelectedRoom.Value = new Room();
music?.EnsurePlayingSomething();
@@ -326,23 +326,23 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
/// The room to copy.
public void OpenCopy(Room room)
{
- Debug.Assert(room.RoomID.Value != null);
+ Debug.Assert(room.RoomID != null);
if (joiningRoomOperation != null)
return;
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
- var req = new GetRoomRequest(room.RoomID.Value.Value);
+ var req = new GetRoomRequest(room.RoomID.Value);
req.Success += r =>
{
// ID must be unset as we use this as a marker for whether this is a client-side (not-yet-created) room or not.
- r.RoomID.Value = null;
+ r.RoomID = null;
// Null out dates because end date is not supported client-side and the settings overlay will populate a duration.
- r.EndDate.Value = null;
- r.Duration.Value = null;
+ r.EndDate = null;
+ r.Duration = null;
Open(r);
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
index 8dc1704fcd..a81425102d 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
@@ -1,8 +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.ComponentModel;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Game.Online.Chat;
using osu.Game.Online.Rooms;
@@ -10,8 +10,6 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{
public partial class MatchChatDisplay : StandAloneChatDisplay
{
- private readonly IBindable channelId = new Bindable();
-
[Resolved]
private ChannelManager? channelManager { get; set; }
@@ -29,23 +27,30 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{
base.LoadComplete();
- // Required for the time being since this component is created prior to the room being joined.
- channelId.BindTo(room.ChannelId);
- channelId.BindValueChanged(_ => updateChannel(), true);
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateChannel();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.ChannelId))
+ updateChannel();
}
private void updateChannel()
{
- if (room.RoomID.Value == null || channelId.Value == 0)
+ if (room.RoomID == null || room.ChannelId == 0)
return;
- Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" });
+ Channel.Value = channelManager?.JoinChannel(new Channel { Id = room.ChannelId, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" });
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+
if (leaveChannelOnDispose)
channelManager?.LeaveChannel(Channel.Value);
}
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs
index 4627cd4072..a7148abcde 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboard.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.ComponentModel;
using System.Threading;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
@@ -13,30 +12,44 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{
public partial class MatchLeaderboard : Leaderboard
{
- [Resolved(typeof(Room), nameof(Room.RoomID))]
- private Bindable roomId { get; set; } = null!;
+ private readonly Room room;
- [BackgroundDependencyLoader]
- private void load()
+ public MatchLeaderboard(Room room)
{
- roomId.BindValueChanged(id =>
- {
- if (id.NewValue == null)
- return;
+ this.room = room;
+ }
- SetScores(null);
- RefetchScores();
- }, true);
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ fetchInitialScores();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.RoomID))
+ fetchInitialScores();
+ }
+
+ private void fetchInitialScores()
+ {
+ if (room.RoomID == null)
+ return;
+
+ SetScores(null);
+ RefetchScores();
}
protected override bool IsOnlineScope => true;
protected override APIRequest? FetchScores(CancellationToken cancellationToken)
{
- if (roomId.Value == null)
+ if (room.RoomID == null)
return null;
- var req = new GetRoomLeaderboardRequest(roomId.Value ?? 0);
+ var req = new GetRoomLeaderboardRequest(room.RoomID.Value);
req.Success += r => Schedule(() =>
{
@@ -52,6 +65,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected override LeaderboardScore CreateDrawableScore(APIUserScoreAggregate model, int index) => new MatchLeaderboardScore(model, index);
protected override LeaderboardScore CreateDrawableTopScore(APIUserScoreAggregate model) => new MatchLeaderboardScore(model, model.Position, false);
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
public enum MatchLeaderboardScope
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs
index 916b799d50..09aed176c4 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -23,7 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected const float TRANSITION_DURATION = 350;
protected const float FIELD_PADDING = 25;
- protected OnlinePlayComposite Settings { get; set; }
+ protected Drawable Settings { get; set; } = null!;
protected override bool BlockScrollInput => false;
@@ -50,7 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected abstract void SelectBeatmap();
- protected abstract OnlinePlayComposite CreateSettings(Room room);
+ protected abstract Drawable CreateSettings(Room room);
protected override void PopIn()
{
diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
index 3bda93c909..0c993f4abf 100644
--- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs
@@ -1,16 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
-using JetBrains.Annotations;
+using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
@@ -21,26 +18,27 @@ namespace osu.Game.Screens.OnlinePlay.Match
{
public partial class DrawableMatchRoom : DrawableRoom
{
- public readonly IBindable SelectedItem = new Bindable();
- public Action OnEdit;
+ public Action? OnEdit;
+
+ public new required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
[Resolved]
- private IAPIProvider api { get; set; }
+ private IAPIProvider api { get; set; } = null!;
- private readonly IBindable host = new Bindable();
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
private readonly bool allowEdit;
-
- [CanBeNull]
- private Drawable editButton;
-
- private BackgroundSprite background;
+ private Drawable? editButton;
public DrawableMatchRoom(Room room, bool allowEdit = true)
: base(room)
{
this.allowEdit = allowEdit;
- host.BindTo(room.Host);
+ base.SelectedItem.BindTo(SelectedItem);
}
[BackgroundDependencyLoader]
@@ -62,17 +60,31 @@ namespace osu.Game.Screens.OnlinePlay.Match
{
base.LoadComplete();
- if (editButton != null)
- host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true);
-
- SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
+ Room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomHost();
}
- protected override Drawable CreateBackground() => background = new BackgroundSprite();
-
- private partial class BackgroundSprite : UpdateableBeatmapBackgroundSprite
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
- protected override double LoadDelay => 0;
+ if (e.PropertyName == nameof(Room.Host))
+ updateRoomHost();
+ }
+
+ private void updateRoomHost()
+ {
+ if (editButton != null)
+ editButton.Alpha = Room.Host?.Equals(api.LocalUser.Value) == true ? 1 : 0;
+ }
+
+ protected override UpdateableBeatmapBackgroundSprite CreateBackground() => base.CreateBackground().With(d =>
+ {
+ d.BackgroundLoadDelay = 0;
+ });
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ Room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index 7c8931c04e..4461cf5402 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
@@ -29,13 +30,13 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
+using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Screens.OnlinePlay.Match
{
[Cached(typeof(IPreviewTrackOwner))]
public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
{
- [Cached(typeof(IBindable))]
public readonly Bindable SelectedItem = new Bindable();
public override bool? ApplyModTrackAdjustments => true;
@@ -60,8 +61,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
///
protected readonly Bindable> UserMods = new Bindable>(Array.Empty());
- protected readonly IBindable RoomId = new Bindable();
-
[Resolved(CanBeNull = true)]
private IOverlayManager overlayManager { get; set; }
@@ -83,6 +82,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; } = null!;
+ [Resolved(canBeNull: true)]
+ private IDialogOverlay dialogOverlay { get; set; }
+
[Cached]
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
@@ -110,8 +112,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
this.allowEdit = allowEdit;
Padding = new MarginPadding { Top = Header.HEIGHT };
-
- RoomId.BindTo(room.RoomID);
}
[BackgroundDependencyLoader]
@@ -164,7 +164,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
new DrawableMatchRoom(Room, allowEdit)
{
OnEdit = () => settingsOverlay.Show(),
- SelectedItem = { BindTarget = SelectedItem }
+ SelectedItem = SelectedItem
}
},
null,
@@ -253,22 +253,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
{
base.LoadComplete();
- RoomId.BindValueChanged(id =>
- {
- if (id.NewValue == null)
- {
- // 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.
- mainContent.Hide();
- settingsOverlay.Show();
- }
- else
- {
- mainContent.Show();
- settingsOverlay.Hide();
- }
- }, true);
-
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
@@ -276,24 +260,38 @@ namespace osu.Game.Screens.OnlinePlay.Match
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap());
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay);
+
+ Room.PropertyChanged += onRoomPropertyChanged;
+ updateSetupState();
}
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ private void onRoomPropertyChanged(object sender, PropertyChangedEventArgs e)
{
- return new CachedModelDependencyContainer(base.CreateChildDependencies(parent))
- {
- Model = { Value = Room }
- };
+ if (e.PropertyName == nameof(Room.RoomID))
+ updateSetupState();
}
- [Resolved(canBeNull: true)]
- private IDialogOverlay dialogOverlay { get; set; }
+ private void updateSetupState()
+ {
+ if (Room.RoomID == null)
+ {
+ // 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.
+ mainContent.Hide();
+ settingsOverlay.Show();
+ }
+ else
+ {
+ mainContent.Show();
+ settingsOverlay.Hide();
+ }
+ }
protected virtual bool IsConnected => api.State.Value == APIState.Online;
public override bool OnBackButton()
{
- if (Room.RoomID.Value == null)
+ if (Room.RoomID == null)
{
if (!ensureExitConfirmed())
return true;
@@ -366,7 +364,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (!IsConnected)
return true;
- bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0;
+ bool hasUnsavedChanges = Room.RoomID == null && Room.Playlist.Count > 0;
if (dialogOverlay == null || !hasUnsavedChanges)
return true;
@@ -539,6 +537,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
base.Dispose(isDisposing);
userModsSelectOverlayRegistration?.Dispose();
+ Room.PropertyChanged -= onRoomPropertyChanged;
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs
index a82fa6e4bc..0d90d44496 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs
@@ -23,6 +23,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public partial class MatchStartControl : CompositeDrawable
{
+ public required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
+
[Resolved]
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
@@ -32,9 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
[Resolved]
private MultiplayerClient client { get; set; } = null!;
- [Resolved]
- private IBindable currentItem { get; set; } = null!;
-
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
private readonly MultiplayerReadyButton readyButton;
private readonly MultiplayerCountdownButton countdownButton;
@@ -94,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
base.LoadComplete();
- currentItem.BindValueChanged(_ => updateState());
+ SelectedItem.BindValueChanged(_ => updateState());
client.RoomUpdated += onRoomUpdated;
client.LoadRequested += onLoadRequested;
updateState();
@@ -210,7 +214,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
readyButton.Enabled.Value = countdownButton.Enabled.Value =
client.Room.State != MultiplayerRoomState.Closed
- && currentItem.Value?.ID == client.Room.Settings.PlaylistItemId
+ && SelectedItem.Value?.ID == client.Room.Settings.PlaylistItemId
&& !client.Room.Playlist.Single(i => i.ID == client.Room.Settings.PlaylistItemId).Expired
&& !operationInProgress.Value;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs
index fcb6480b58..2b592bd8b9 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs
@@ -1,10 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Game.Online.Rooms;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
@@ -13,6 +13,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private const float ready_button_width = 600;
private const float spectate_button_width = 200;
+ public required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
+
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
+
public MultiplayerMatchFooter()
{
RelativeSizeAxes = Axes.Both;
@@ -22,17 +30,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
RelativeSizeAxes = Axes.Both,
Content = new[]
{
- new Drawable[]
+ new Drawable?[]
{
null,
new MultiplayerSpectateButton
{
RelativeSizeAxes = Axes.Both,
+ SelectedItem = selectedItem
},
null,
new MatchStartControl
{
RelativeSizeAxes = Axes.Both,
+ SelectedItem = selectedItem
},
null
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
index 5446211ced..1dbef079d4 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs
@@ -4,6 +4,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
@@ -28,14 +29,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public partial class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay
{
- private MatchSettings settings = null!;
+ public required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
protected override OsuButton SubmitButton => settings.ApplyButton;
+ protected override bool IsLoading => ongoingOperationTracker.InProgress.Value;
[Resolved]
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
- protected override bool IsLoading => ongoingOperationTracker.InProgress.Value;
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
+ private MatchSettings settings = null!;
public MultiplayerMatchSettingsOverlay(Room room)
: base(room)
@@ -44,19 +51,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
protected override void SelectBeatmap() => settings.SelectBeatmap();
- protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room)
+ protected override Drawable CreateSettings(Room room) => settings = new MatchSettings(room)
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
- SettingsApplied = Hide
+ SettingsApplied = Hide,
+ SelectedItem = { BindTarget = SelectedItem }
};
- protected partial class MatchSettings : OnlinePlayComposite
+ protected partial class MatchSettings : CompositeDrawable
{
private const float disabled_alpha = 0.2f;
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
+ public readonly Bindable SelectedItem = new Bindable();
public Action? SettingsApplied;
public OsuTextBox NameField = null!;
@@ -66,7 +75,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
public OsuTextBox PasswordTextBox = null!;
public OsuCheckbox AutoSkipCheckbox = null!;
public RoundedButton ApplyButton = null!;
-
public OsuSpriteText ErrorText = null!;
private OsuEnumDropdown startModeDropdown = null!;
@@ -270,7 +278,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
drawablePlaylist = new DrawableRoomPlaylist
{
RelativeSizeAxes = Axes.X,
- Height = DrawableRoomPlaylistItem.HEIGHT
+ Height = DrawableRoomPlaylistItem.HEIGHT,
+ SelectedItem = { BindTarget = SelectedItem }
},
selectBeatmapButton = new RoundedButton
{
@@ -316,7 +325,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING },
Children = new Drawable[]
{
- ApplyButton = new CreateOrUpdateButton
+ ApplyButton = new CreateOrUpdateButton(room)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
@@ -343,14 +352,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
};
TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true);
- RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true);
- Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true);
- MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true);
- RoomID.BindValueChanged(roomId => playlistContainer.Alpha = roomId.NewValue == null ? 1 : 0, true);
- Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true);
- QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true);
- AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true);
- AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true);
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
operationInProgress.BindValueChanged(v =>
@@ -366,15 +367,88 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
base.LoadComplete();
- drawablePlaylist.Items.BindTo(Playlist);
- drawablePlaylist.SelectedItem.BindTo(CurrentPlaylistItem);
+ room.PropertyChanged += onRoomPropertyChanged;
+
+ updateRoomName();
+ updateRoomType();
+ updateRoomQueueMode();
+ updateRoomPassword();
+ updateRoomAutoSkip();
+ updateRoomMaxParticipants();
+ updateRoomAutoStartDuration();
+ updateRoomPlaylist();
+
+ drawablePlaylist.Items.BindCollectionChanged((_, __) => room.Playlist = drawablePlaylist.Items.ToArray());
}
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ switch (e.PropertyName)
+ {
+ case nameof(Room.Name):
+ updateRoomName();
+ break;
+
+ case nameof(Room.Type):
+ updateRoomName();
+ break;
+
+ case nameof(Room.QueueMode):
+ updateRoomQueueMode();
+ break;
+
+ case nameof(Room.Password):
+ updateRoomPassword();
+ break;
+
+ case nameof(Room.AutoSkip):
+ updateRoomAutoSkip();
+ break;
+
+ case nameof(Room.MaxParticipants):
+ updateRoomMaxParticipants();
+ break;
+
+ case nameof(Room.AutoStartDuration):
+ updateRoomAutoStartDuration();
+ break;
+
+ case nameof(Room.Playlist):
+ updateRoomPlaylist();
+ break;
+ }
+ }
+
+ private void updateRoomName()
+ => NameField.Text = room.Name;
+
+ private void updateRoomType()
+ => TypePicker.Current.Value = room.Type;
+
+ private void updateRoomQueueMode()
+ => QueueModeDropdown.Current.Value = room.QueueMode;
+
+ private void updateRoomPassword()
+ => PasswordTextBox.Text = room.Password ?? string.Empty;
+
+ private void updateRoomAutoSkip()
+ => AutoSkipCheckbox.Current.Value = room.AutoSkip;
+
+ private void updateRoomMaxParticipants()
+ => MaxParticipantsField.Text = room.MaxParticipants?.ToString();
+
+ private void updateRoomAutoStartDuration()
+ => typeLabel.Text = room.AutoStartDuration.GetLocalisableDescription();
+
+ private void updateRoomPlaylist()
+ => drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
+
protected override void Update()
{
base.Update();
- ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value;
+ ApplyButton.Enabled.Value = room.Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value;
+ playlistContainer.Alpha = room.RoomID == null ? 1 : 0;
}
private void apply()
@@ -387,8 +461,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
Debug.Assert(applyingSettingsOperation == null);
applyingSettingsOperation = ongoingOperationTracker.BeginOperation();
- TimeSpan autoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value);
-
// If the client is already in a room, update via the client.
// Otherwise, update the room directly in preparation for it to be submitted to the API on match creation.
if (client.Room != null)
@@ -398,7 +470,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
password: PasswordTextBox.Text,
matchType: TypePicker.Current.Value,
queueMode: QueueModeDropdown.Current.Value,
- autoStartDuration: autoStartDuration,
+ autoStartDuration: TimeSpan.FromSeconds((int)startModeDropdown.Current.Value),
autoSkip: AutoSkipCheckbox.Current.Value)
.ContinueWith(t => Schedule(() =>
{
@@ -410,17 +482,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
}
else
{
- room.Name.Value = NameField.Text;
- room.Type.Value = TypePicker.Current.Value;
- room.Password.Value = PasswordTextBox.Current.Value;
- room.QueueMode.Value = QueueModeDropdown.Current.Value;
- room.AutoStartDuration.Value = autoStartDuration;
- room.AutoSkip.Value = AutoSkipCheckbox.Current.Value;
+ room.Name = NameField.Text;
+ room.Type = TypePicker.Current.Value;
+ room.Password = PasswordTextBox.Current.Value;
+ room.QueueMode = QueueModeDropdown.Current.Value;
+ room.AutoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value);
+ room.AutoSkip = AutoSkipCheckbox.Current.Value;
if (int.TryParse(MaxParticipantsField.Text, out int max))
- room.MaxParticipants.Value = max;
+ room.MaxParticipants = max;
else
- room.MaxParticipants.Value = null;
+ room.MaxParticipants = null;
manager.CreateRoom(room, onSuccess, onError);
}
@@ -448,7 +520,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.";
- CurrentPlaylistItem.Value.MarkInvalid();
+ SelectedItem.Value?.MarkInvalid();
}
else
{
@@ -460,17 +532,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
applyingSettingsOperation.Dispose();
applyingSettingsOperation = null;
});
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
}
public partial class CreateOrUpdateButton : RoundedButton
{
- [Resolved(typeof(Room), nameof(Room.RoomID))]
- private Bindable roomId { get; set; } = null!;
+ private readonly Room room;
- protected override void LoadComplete()
+ public CreateOrUpdateButton(Room room)
{
- base.LoadComplete();
- roomId.BindValueChanged(id => Text = id.NewValue == null ? "Create" : "Update", true);
+ this.room = room;
}
[BackgroundDependencyLoader]
@@ -478,6 +554,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
BackgroundColour = colours.YellowDark;
}
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Text = room.RoomID == null ? "Create" : "Update";
+ }
}
private enum StartMode
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs
index 92edc9b979..905a2bf31b 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs
@@ -21,6 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public partial class MultiplayerSpectateButton : CompositeDrawable
{
+ public required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
+
[Resolved]
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
@@ -30,13 +36,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
[Resolved]
private MultiplayerClient client { get; set; } = null!;
- [Resolved]
- private IBindable currentItem { get; set; } = null!;
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
+ private readonly RoundedButton button;
private IBindable operationInProgress = null!;
- private readonly RoundedButton button;
-
public MultiplayerSpectateButton()
{
InternalChild = button = new RoundedButton
@@ -71,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
base.LoadComplete();
- currentItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true);
+ SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true);
client.RoomUpdated += onRoomUpdated;
updateState();
}
@@ -117,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private void checkForAutomaticDownload()
{
- PlaylistItem? item = currentItem.Value;
+ PlaylistItem? item = SelectedItem.Value;
downloadCheckCancellation?.Cancel();
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs
index 8ba85019d5..9feee0ae41 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs
@@ -19,6 +19,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
{
public readonly Bindable DisplayMode = new Bindable();
+ public required Bindable SelectedItem
+ {
+ get => selectedItem;
+ set => selectedItem.Current = value;
+ }
+
///
/// Invoked when an item requests to be edited.
///
@@ -27,14 +33,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
[Resolved]
private MultiplayerClient client { get; set; } = null!;
- [Resolved]
- private IBindable currentItem { get; set; } = null!;
-
+ private readonly Room room;
+ private readonly BindableWithCurrent selectedItem = new BindableWithCurrent();
private MultiplayerPlaylistTabControl playlistTabControl = null!;
private MultiplayerQueueList queueList = null!;
private MultiplayerHistoryList historyList = null!;
private bool firstPopulation = true;
+ public MultiplayerPlaylist(Room room)
+ {
+ this.room = room;
+ }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -55,17 +65,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
Masking = true,
Children = new Drawable[]
{
- queueList = new MultiplayerQueueList
+ queueList = new MultiplayerQueueList(room)
{
RelativeSizeAxes = Axes.Both,
- SelectedItem = { BindTarget = currentItem },
+ SelectedItem = { BindTarget = selectedItem },
RequestEdit = item => RequestEdit?.Invoke(item)
},
historyList = new MultiplayerHistoryList
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
- SelectedItem = { BindTarget = currentItem }
+ SelectedItem = { BindTarget = selectedItem }
}
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs
index 77d82c4347..04bb9b69e6 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs
@@ -1,12 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API;
@@ -21,28 +20,49 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
///
public partial class MultiplayerQueueList : DrawableRoomPlaylist
{
- public MultiplayerQueueList()
+ private readonly Room room;
+
+ private QueueFillFlowContainer flow = null!;
+
+ public MultiplayerQueueList(Room room)
{
+ this.room = room;
ShowItemOwners = true;
}
- protected override FillFlowContainer> CreateListFillFlowContainer() => new QueueFillFlowContainer
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ room.PropertyChanged += onRoomPropertyChanged;
+ updateRoomPlaylist();
+ }
+
+ private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Room.Playlist))
+ updateRoomPlaylist();
+ }
+
+ private void updateRoomPlaylist()
+ => flow.InvalidateLayout();
+
+ protected override FillFlowContainer> CreateListFillFlowContainer() => flow = new QueueFillFlowContainer
{
Spacing = new Vector2(0, 2)
};
protected override DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new QueuePlaylistItem(item);
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ room.PropertyChanged -= onRoomPropertyChanged;
+ }
+
private partial class QueueFillFlowContainer : FillFlowContainer>
{
- [Resolved(typeof(Room), nameof(Room.Playlist))]
- private BindableList roomPlaylist { get; set; }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- roomPlaylist.BindCollectionChanged((_, _) => InvalidateLayout());
- }
+ public new void InvalidateLayout() => base.InvalidateLayout();
public override IEnumerable FlowingChildren => base.FlowingChildren.OfType>().OrderBy(item => item.Model.PlaylistOrder);
}
@@ -50,10 +70,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
private partial class QueuePlaylistItem : DrawableRoomPlaylistItem
{
[Resolved]
- private IAPIProvider api { get; set; }
+ private IAPIProvider api { get; set; } = null!;
[Resolved]
- private MultiplayerClient multiplayerClient { get; set; }
+ private MultiplayerClient multiplayerClient { get; set; } = null!;
public QueuePlaylistItem(PlaylistItem item)
: base(item)
@@ -91,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
{
base.Dispose(isDisposing);
- if (multiplayerClient != null)
+ if (multiplayerClient.IsNotNull())
multiplayerClient.RoomUpdated -= onRoomUpdated;
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
index a3a6fd2d8e..a5cc267569 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs
@@ -73,8 +73,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override Room CreateNewRoom() => new Room
{
- Name = { Value = $"{api.LocalUser}'s awesome room" },
- Type = { Value = MatchType.HeadToHead },
+ Name = $"{api.LocalUser}'s awesome room",
+ Type = MatchType.HeadToHead,
};
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
index 873a1b0d50..4e03c19095 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs
@@ -24,6 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved]
private OngoingOperationTracker operationTracker { get; set; } = null!;
+ private readonly Room room;
private readonly IBindable operationInProgress = new Bindable();
private readonly PlaylistItem? itemToEdit;
@@ -38,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public MultiplayerMatchSongSelect(Room room, PlaylistItem? itemToEdit = null)
: base(room, itemToEdit)
{
+ this.room = room;
this.itemToEdit = itemToEdit;
}
@@ -111,8 +113,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
}
else
{
- Playlist.Clear();
- Playlist.Add(item);
+ room.Playlist = [item];
this.Exit();
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index a37314de0e..65355f02e5 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -1,13 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
@@ -45,17 +44,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public override string ShortTitle => "room";
[Resolved]
- private MultiplayerClient client { get; set; }
+ private MultiplayerClient client { get; set; } = null!;
[Resolved(canBeNull: true)]
- private OsuGame game { get; set; }
+ private OsuGame? game { get; set; }
- private AddItemButton addItemButton;
+ private AddItemButton addItemButton = null!;
public MultiplayerMatchSubScreen(Room room)
: base(room)
{
- Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
+ Title = room.RoomID == null ? "New room" : room.Name;
Activity.Value = new UserActivity.InLobby(room);
}
@@ -95,7 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
},
Content = new[]
{
- new Drawable[]
+ new Drawable?[]
{
// Participants column
new GridContainer
@@ -139,10 +138,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
null,
new Drawable[]
{
- new MultiplayerPlaylist
+ new MultiplayerPlaylist(Room)
{
RelativeSizeAxes = Axes.Both,
- RequestEdit = OpenSongSelection
+ RequestEdit = OpenSongSelection,
+ SelectedItem = SelectedItem
}
},
new[]
@@ -220,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
/// Opens the song selection screen to add or edit an item.
///
/// An optional playlist item to edit. If null, a new item will be added instead.
- internal void OpenSongSelection(PlaylistItem itemToEdit = null)
+ internal void OpenSongSelection(PlaylistItem? itemToEdit = null)
{
if (!this.IsCurrentScreen())
return;
@@ -228,9 +228,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit));
}
- protected override Drawable CreateFooter() => new MultiplayerMatchFooter();
+ protected override Drawable CreateFooter() => new MultiplayerMatchFooter
+ {
+ SelectedItem = SelectedItem
+ };
- protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room);
+ protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room)
+ {
+ SelectedItem = SelectedItem
+ };
protected override void UpdateMods()
{
@@ -245,7 +251,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
}
[Resolved(canBeNull: true)]
- private IDialogOverlay dialogOverlay { get; set; }
+ private IDialogOverlay? dialogOverlay { get; set; }
private bool exitConfirmed;
@@ -275,8 +281,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
return base.OnExiting(e);
}
- private ModSettingChangeTracker modSettingChangeTracker;
- private ScheduledDelegate debouncedModSettingsUpdate;
+ private ModSettingChangeTracker? modSettingChangeTracker;
+ private ScheduledDelegate? debouncedModSettingsUpdate;
private void onUserModsChanged(ValueChangedEvent> mods)
{
@@ -352,7 +358,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Activity.Value = new UserActivity.InLobby(Room);
}
- private bool localUserCanAddItem => client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly;
+ private bool localUserCanAddItem => client.IsHost || Room.QueueMode != QueueMode.HostOnly;
private void updateCurrentItem()
{
@@ -422,7 +428,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
return;
// If there's only one playlist item and we are the host, assume we want to change it. Else add a new one.
- PlaylistItem itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null;
+ PlaylistItem? itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null;
OpenSongSelection(itemToEdit);
@@ -434,7 +440,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
base.Dispose(isDisposing);
- if (client != null)
+ if (client.IsNotNull())
{
client.RoomUpdated -= onRoomUpdated;
client.LoadRequested -= onLoadRequested;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
index a50f3440f5..111b453adb 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
@@ -1,14 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Graphics.UserInterface;
@@ -29,17 +28,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
[Resolved]
- private MultiplayerClient client { get; set; }
+ private MultiplayerClient client { get; set; } = null!;
- private IBindable isConnected;
+ private IBindable isConnected = null!;
private readonly TaskCompletionSource resultsReady = new TaskCompletionSource();
-
private readonly MultiplayerRoomUser[] users;
- private LoadingLayer loadingDisplay;
-
- private MultiplayerGameplayLeaderboard multiplayerLeaderboard;
+ private LoadingLayer loadingDisplay = null!;
+ private MultiplayerGameplayLeaderboard multiplayerLeaderboard = null!;
///
/// Construct a multiplayer player.
@@ -53,8 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
AllowPause = false,
AllowRestart = false,
AllowFailAnimation = false,
- AllowSkipping = room.AutoSkip.Value,
- AutomaticallySkipIntro = room.AutoSkip.Value,
+ AllowSkipping = room.AutoSkip,
+ AutomaticallySkipIntro = room.AutoSkip,
AlwaysShowLeaderboard = true,
})
{
@@ -153,7 +150,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
GameplayClockContainer.Reset();
}
- private void failAndBail(string message = null)
+ private void failAndBail(string? message = null)
{
if (!string.IsNullOrEmpty(message))
Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important);
@@ -196,14 +193,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override ResultsScreen CreateResults(ScoreInfo score)
{
- Debug.Assert(Room.RoomID.Value != null);
+ Debug.Assert(Room.RoomID != null);
return multiplayerLeaderboard.TeamScores.Count == 2
- ? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem, multiplayerLeaderboard.TeamScores)
+ ? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value, PlaylistItem, multiplayerLeaderboard.TeamScores)
{
ShowUserStatistics = true,
}
- : new MultiplayerResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem)
+ : new MultiplayerResultsScreen(score, Room.RoomID.Value, PlaylistItem)
{
ShowUserStatistics = true
};
@@ -213,7 +210,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
base.Dispose(isDisposing);
- if (client != null)
+ if (client.IsNotNull())
{
client.GameplayStarted -= onGameplayStarted;
client.ResultsReady -= onResultsReady;
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
index 5f51ccc8d4..e16582a6e1 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
@@ -18,12 +16,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public partial class MultiplayerRoomManager : RoomManager
{
[Resolved]
- private MultiplayerClient multiplayerClient { get; set; }
+ 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.Value, onSuccess, onError), onError);
+ 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)
+ public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null)
{
if (!multiplayerClient.IsConnected.Value)
{
@@ -33,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// 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.Status.Value is RoomStatusEnded)
+ if (room.Status is RoomStatusEnded)
{
onError?.Invoke("Cannot join an ended room.");
return;
@@ -51,9 +49,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
multiplayerClient.LeaveRoom();
}
- private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null)
+ private void joinMultiplayerRoom(Room room, string? password, Action? onSuccess = null, Action? onError = null)
{
- Debug.Assert(room.RoomID.Value != null);
+ Debug.Assert(room.RoomID != null);
multiplayerClient.JoinRoom(room, password).ContinueWith(t =>
{
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs
deleted file mode 100644
index 5be5c4b4f4..0000000000
--- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-#nullable disable
-
-using System;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Online.Multiplayer;
-using osu.Game.Online.Rooms;
-using osu.Game.Screens.OnlinePlay.Match;
-
-namespace osu.Game.Screens.OnlinePlay
-{
- ///
- /// A that exposes bindables for properties.
- ///
- public partial class OnlinePlayComposite : CompositeDrawable
- {
- [Resolved(typeof(Room))]
- protected Bindable RoomID { get; private set; }
-
- [Resolved(typeof(Room), nameof(Room.Name))]
- protected Bindable RoomName { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Host { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Status { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Type { get; private set; }
-
- ///
- /// The currently selected item in the , or the current item from
- /// if this is not within a .
- ///
- [Resolved(typeof(Room))]
- protected Bindable CurrentPlaylistItem { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable PlaylistItemStats { get; private set; }
-
- [Resolved(typeof(Room))]
- protected BindableList Playlist { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable DifficultyRange { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Category { get; private set; }
-
- [Resolved(typeof(Room))]
- protected BindableList RecentParticipants { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable ParticipantCount { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable MaxParticipants { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable MaxAttempts { get; private set; }
-
- [Resolved(typeof(Room))]
- public Bindable UserScore { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable StartDate { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable EndDate { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Availability { get; private set; }
-
- [Resolved(typeof(Room))]
- public Bindable Password { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable Duration { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable QueueMode { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable AutoStartDuration { get; private set; }
-
- [Resolved(typeof(Room))]
- protected Bindable AutoSkip { get; private set; }
-
- [Resolved(CanBeNull = true)]
- private IBindable subScreenSelectedItem { get; set; }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- subScreenSelectedItem?.BindValueChanged(_ => UpdateSelectedItem());
- Playlist.BindCollectionChanged((_, _) => UpdateSelectedItem(), true);
- }
-
- protected void UpdateSelectedItem()
- {
- // null room ID means this is a room in the process of being created.
- if (RoomID.Value == null)
- CurrentPlaylistItem.Value = Playlist.GetCurrentItem();
- else if (subScreenSelectedItem != null)
- CurrentPlaylistItem.Value = subScreenSelectedItem.Value;
- }
- }
-}
diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
index a8dfece916..f6b6dfd3ab 100644
--- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
+++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
@@ -32,9 +32,6 @@ namespace osu.Game.Screens.OnlinePlay
public override bool AllowEditing => false;
- [Resolved(typeof(Room), nameof(Room.Playlist))]
- protected BindableList Playlist { get; private set; } = null!;
-
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
index e1d747c3b0..d66b4f844c 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd