diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cb45447ed5..d75f09f184 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -114,7 +114,7 @@ jobs:
dotnet-version: "8.0.x"
- name: Install .NET workloads
- run: dotnet workload install maui-android
+ run: dotnet workload install android
- name: Compile
run: dotnet build -c Debug osu.Android.slnf
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 +92,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 +115,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 +165,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 +194,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 +218,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 +241,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 +255,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
- });
+ ];
});
ClickButtonWhenEnabled();
@@ -286,24 +277,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();
@@ -323,8 +316,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
{
[Resolved(canBeNull: true)]
- [CanBeNull]
- private IDialogOverlay dialogOverlay { get; set; }
+ private IDialogOverlay? dialogOverlay { get; set; }
public TestMultiplayerMatchSubScreen(Room room)
: base(room)
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..36f5bba384 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..3ef2e4ecf4 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/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
index f030466fff..076c2c3cdd 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.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 NUnit.Framework;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
@@ -16,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestDisplayResults()
{
- MultiplayerResultsScreen screen = null;
+ MultiplayerResultsScreen screen = null!;
AddStep("show results screen", () =>
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs
index 5ae5d1e228..1429f86164 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/TestSceneMultiplayerSpectatorPlayerGrid.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs
index 8fd05dcaa9..2f461ad706 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.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 NUnit.Framework;
using osu.Framework.Graphics;
@@ -16,7 +14,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestSceneMultiplayerSpectatorPlayerGrid : OsuManualInputManagerTestScene
{
- private PlayerGrid grid;
+ private PlayerGrid grid = null!;
[SetUp]
public void Setup() => Schedule(() =>
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
index 68fd39a066..f77b6e8c68 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.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.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Bindables;
@@ -32,7 +30,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[TestCase(1048576, 1048576)]
public void TestDisplayTeamResults(int team1Score, int team2Score)
{
- MultiplayerResultsScreen screen = null;
+ MultiplayerResultsScreen screen = null!;
AddStep("show results screen", () =>
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs
index ae27db0dd1..cd41884ba7 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.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;
@@ -28,18 +26,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestScenePlaylistsRoomSettingsPlaylist : OnlinePlayTestScene
{
- private TestPlaylist playlist;
+ private TestPlaylist playlist = null!;
[Test]
public void TestItemRemovedOnDeletion()
{
- PlaylistItem selectedItem = null;
+ PlaylistItem selectedItem = null!;
createPlaylist();
moveToItem(0);
AddStep("click", () => InputManager.Click(MouseButton.Left));
- AddStep("retrieve selection", () => selectedItem = playlist.SelectedItem.Value);
+ AddStep("retrieve selection", () => selectedItem = playlist.SelectedItem.Value!);
moveToDeleteButton(0);
AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
@@ -122,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.MoveMouseTo(item.ChildrenOfType().ElementAt(0), offset);
});
- private void createPlaylist(Action setupPlaylist = null)
+ private void createPlaylist(Action? setupPlaylist = null)
{
AddStep("create playlist", () =>
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
index cc78bed5de..fa1909254a 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.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,9 +25,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public partial class TestScenePlaylistsSongSelect : OnlinePlayTestScene
{
- private BeatmapManager manager;
-
- private TestPlaylistsSongSelect songSelect;
+ private BeatmapManager manager = null!;
+ private TestPlaylistsSongSelect songSelect = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -60,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedMods.Value = Array.Empty();
});
- AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value)));
+ AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value!)));
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
}
@@ -68,46 +65,41 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestItemAddedIfEmptyOnStart()
{
AddStep("finalise selection", () => songSelect.FinaliseSelection());
- AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
+ AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
}
[Test]
public void TestItemAddedWhenCreateNewItemClicked()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
+ 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);
+ AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
}
[Test]
public void TestAddSameItemMultipleTimes()
{
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
- AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2);
+ 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());
- AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2);
+ AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
+ AddAssert("new item has id 2", () => SelectedRoom.Value!.Playlist.Last().ID == 2);
}
///
@@ -117,19 +109,19 @@ 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", () =>
{
- var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
});
AddAssert("item 2 has rate 2", () =>
{
- var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
+ var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(2, mod.SpeedChange.Value);
});
}
@@ -140,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestGlobalModInstancesNotRetained()
{
- OsuModDoubleTime mod = null;
+ OsuModDoubleTime mod = null!;
AddStep("set dt mod and store", () =>
{
@@ -150,12 +142,12 @@ 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", () =>
{
- var m = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
+ var m = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
});
}
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..88afef7de2 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/Online/TestSceneReplayMissingBeatmap.cs b/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs
index 60197e0eb7..b986901dcf 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs
@@ -40,15 +40,13 @@ namespace osu.Game.Tests.Visual.Online
AddStep("import score", () =>
{
- using (var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr"))
- {
- var importTask = new ImportTask(resourceStream, "replay.osr");
+ var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr");
+ var importTask = new ImportTask(resourceStream, "replay.osr");
- Game.ScoreManager.Import(new[] { importTask });
- }
+ Game.ScoreManager.Import(new[] { importTask });
});
- AddUntilStep("Replay missing notification show", () => Game.Notifications.ChildrenOfType().Any());
+ AddUntilStep("Replay missing notification shown", () => Game.Notifications.ChildrenOfType().Any());
}
[Test]
@@ -58,15 +56,13 @@ namespace osu.Game.Tests.Visual.Online
AddStep("import score", () =>
{
- using (var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr"))
- {
- var importTask = new ImportTask(resourceStream, "replay.osr");
+ var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr");
+ var importTask = new ImportTask(resourceStream, "replay.osr");
- Game.ScoreManager.Import(new[] { importTask });
- }
+ Game.ScoreManager.Import(new[] { importTask });
});
- AddUntilStep("Replay missing notification not show", () => !Game.Notifications.ChildrenOfType().Any());
+ AddUntilStep("Replay missing notification not shown", () => !Game.Notifications.ChildrenOfType().Any());
}
private void setupBeatmapResponse(APIBeatmap b)
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index 0c536cb1d4..8c8dc8d69a 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.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 NUnit.Framework;
using osu.Framework.Bindables;
@@ -21,7 +19,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
- private TestLoungeSubScreen loungeScreen;
+ private TestLoungeSubScreen loungeScreen = null!;
public override void SetUpSteps()
{
@@ -97,7 +95,7 @@ namespace osu.Game.Tests.Visual.Playlists
private partial class TestLoungeSubScreen : PlaylistsLoungeSubScreen
{
- public new Bindable SelectedRoom => base.SelectedRoom;
+ public new Bindable SelectedRoom => base.SelectedRoom;
}
}
}
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 9f7b20ad43..5868331451 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();
@@ -34,7 +32,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
SelectedRoom.Value = new Room();
- Child = settings = new TestRoomSettings(SelectedRoom.Value)
+ Child = settings = new TestRoomSettings(SelectedRoom.Value!)
{
RelativeSizeAxes = Axes.Both,
State = { Value = Visibility.Visible }
@@ -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}";
@@ -109,13 +107,13 @@ namespace osu.Game.Tests.Visual.Playlists
});
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
- AddAssert("playlist item valid", () => SelectedRoom.Value.Playlist[0].Valid.Value);
+ AddAssert("playlist item valid", () => SelectedRoom.Value!.Playlist[0].Valid.Value);
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
AddAssert("error displayed", () => settings.ErrorText.IsPresent);
AddAssert("error has custom text", () => settings.ErrorText.Text != errorMessage);
- AddAssert("playlist item marked invalid", () => !SelectedRoom.Value.Playlist[0].Valid.Value);
+ AddAssert("playlist item marked invalid", () => !SelectedRoom.Value!.Playlist[0].Valid.Value);
}
[Test]
@@ -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..c60b208ffc 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/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
index 7527647b9c..5977e67b0e 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.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.Linq;
using System.Net;
-using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
@@ -34,14 +31,14 @@ namespace osu.Game.Tests.Visual.Playlists
private const int scores_per_result = 10;
private const int real_user_position = 200;
- private TestResultsScreen resultsScreen;
+ private TestResultsScreen resultsScreen = null!;
private int lowestScoreId; // Score ID of the lowest score in the list.
private int highestScoreId; // Score ID of the highest score in the list.
private bool requestComplete;
private int totalCount;
- private ScoreInfo userScore;
+ private ScoreInfo userScore = null!;
[SetUpSteps]
public override void SetUpSteps()
@@ -205,7 +202,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddAssert("placeholder shown", () => this.ChildrenOfType().Count(), () => Is.EqualTo(1));
}
- private void createResults(Func getScore = null)
+ private void createResults(Func? getScore = null)
{
AddStep("load results", () =>
{
@@ -229,7 +226,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddWaitStep("wait for display", 5);
}
- private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false, bool noScores = false) => ((DummyAPIAccess)API).HandleRequest = request =>
+ private void bindHandler(bool delayed = false, ScoreInfo? userScore = null, bool failRequests = false, bool noScores = false) => ((DummyAPIAccess)API).HandleRequest = request =>
{
// pre-check for requests we should be handling (as they are scheduled below).
switch (request)
@@ -286,7 +283,7 @@ namespace osu.Game.Tests.Visual.Playlists
req.TriggerFailure(new WebException("Failed."));
}
- private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
+ private MultiplayerScore createUserResponse(ScoreInfo userScore)
{
var multiplayerUserScore = new MultiplayerScore
{
@@ -420,7 +417,7 @@ namespace osu.Game.Tests.Visual.Playlists
public new LoadingSpinner RightSpinner => base.RightSpinner;
public new ScorePanelList ScorePanelList => base.ScorePanelList;
- public TestResultsScreen([CanBeNull] ScoreInfo score, int roomId, PlaylistItem playlistItem)
+ public TestResultsScreen(ScoreInfo? score, int roomId, PlaylistItem playlistItem)
: base(score, roomId, playlistItem)
{
AllowRetry = true;
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs
index 1636a3d4b8..0270840597 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,19 +213,17 @@ 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
{
- public new Bindable SelectedItem => base.SelectedItem;
+ public new Bindable SelectedItem => base.SelectedItem;
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/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
new file mode 100644
index 0000000000..4306fc1e6a
--- /dev/null
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
@@ -0,0 +1,41 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using NUnit.Framework;
+using osu.Framework.Screens;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Online.Rooms;
+using osu.Game.Online.Rooms.RoomStatuses;
+using osu.Game.Screens.OnlinePlay.Playlists;
+using osu.Game.Tests.Visual.OnlinePlay;
+
+namespace osu.Game.Tests.Visual.Playlists
+{
+ public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
+ {
+ protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
+
+ [Test]
+ public void TestStatusUpdateOnEnter()
+ {
+ Room room = null!;
+ PlaylistsRoomSubScreen roomScreen = null!;
+
+ AddStep("create room", () =>
+ {
+ RoomManager.AddRoom(room = new Room
+ {
+ Name = @"Test Room",
+ Host = new APIUser { Username = @"Host" },
+ Category = RoomCategory.Normal,
+ EndDate = DateTimeOffset.Now.AddMinutes(-1)
+ });
+ });
+
+ AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
+ AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
+ AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs
index e3a6fca319..5acd6cb084 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapAttributeText.cs
@@ -73,10 +73,10 @@ namespace osu.Game.Tests.Visual.UserInterface
});
});
- [TestCase(BeatmapAttribute.CircleSize, "Circle Size: 1.00")]
- [TestCase(BeatmapAttribute.HPDrain, "HP Drain: 2.00")]
- [TestCase(BeatmapAttribute.Accuracy, "Accuracy: 3.00")]
- [TestCase(BeatmapAttribute.ApproachRate, "Approach Rate: 4.00")]
+ [TestCase(BeatmapAttribute.CircleSize, "Circle Size: 1")]
+ [TestCase(BeatmapAttribute.HPDrain, "HP Drain: 2")]
+ [TestCase(BeatmapAttribute.Accuracy, "Accuracy: 3")]
+ [TestCase(BeatmapAttribute.ApproachRate, "Approach Rate: 4")]
[TestCase(BeatmapAttribute.Title, "Title: _Title")]
[TestCase(BeatmapAttribute.Artist, "Artist: _Artist")]
[TestCase(BeatmapAttribute.Creator, "Creator: _Creator")]
@@ -121,15 +121,15 @@ namespace osu.Game.Tests.Visual.UserInterface
Difficulty =
{
ApproachRate = 10,
- CircleSize = 9
+ CircleSize = 9.5f
}
}
}));
- test(BeatmapAttribute.BPM, new OsuModDoubleTime(), "BPM: 100.00", "BPM: 150.00");
+ test(BeatmapAttribute.BPM, new OsuModDoubleTime(), "BPM: 100", "BPM: 150");
test(BeatmapAttribute.Length, new OsuModDoubleTime(), "Length: 00:30", "Length: 00:20");
- test(BeatmapAttribute.ApproachRate, new OsuModDoubleTime(), "Approach Rate: 10.00", "Approach Rate: 11.00");
- test(BeatmapAttribute.CircleSize, new OsuModHardRock(), "Circle Size: 9.00", "Circle Size: 10.00");
+ test(BeatmapAttribute.ApproachRate, new OsuModDoubleTime(), "Approach Rate: 10", "Approach Rate: 11");
+ test(BeatmapAttribute.CircleSize, new OsuModHardRock(), "Circle Size: 9.5", "Circle Size: 10");
void test(BeatmapAttribute attribute, Mod mod, string before, string after)
{
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/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs
index 164ec558a4..85af1d383d 100644
--- a/osu.Game/Collections/DrawableCollectionList.cs
+++ b/osu.Game/Collections/DrawableCollectionList.cs
@@ -21,6 +21,12 @@ namespace osu.Game.Collections
///
public partial class DrawableCollectionList : OsuRearrangeableListContainer>
{
+ public new MarginPadding Padding
+ {
+ get => base.Padding;
+ set => base.Padding = value;
+ }
+
protected override ScrollContainer CreateScrollContainer() => scroll = new Scroll();
[Resolved]
@@ -34,6 +40,12 @@ namespace osu.Game.Collections
public IEnumerable OrderedItems => flow.FlowingChildren;
+ public string SearchTerm
+ {
+ get => flow.SearchTerm;
+ set => flow.SearchTerm = value;
+ }
+
protected override FillFlowContainer>> CreateListFillFlowContainer() => flow = new Flow
{
DragActive = { BindTarget = DragActive }
@@ -46,6 +58,26 @@ namespace osu.Game.Collections
realmSubscription = realm.RegisterForNotifications(r => r.All().OrderBy(c => c.Name), collectionsChanged);
}
+ ///
+ /// When non-null, signifies that a new collection was created and should be presented to the user.
+ ///
+ private Guid? lastCreated;
+
+ protected override void OnItemsChanged()
+ {
+ base.OnItemsChanged();
+
+ if (lastCreated != null)
+ {
+ var createdItem = flow.Children.SingleOrDefault(item => item.Model.Value.ID == lastCreated);
+
+ if (createdItem != null)
+ scroll.ScrollTo(createdItem);
+
+ lastCreated = null;
+ }
+ }
+
private void collectionsChanged(IRealmCollection collections, ChangeSet? changes)
{
if (changes == null)
@@ -60,7 +92,11 @@ namespace osu.Game.Collections
foreach (int i in changes.InsertedIndices)
Items.Insert(i, collections[i].ToLive(realm));
+ if (changes.InsertedIndices.Length == 1)
+ lastCreated = collections[changes.InsertedIndices[0]].ID;
+
foreach (int i in changes.NewModifiedIndices)
+
{
var updatedItem = collections[i];
@@ -104,8 +140,7 @@ namespace osu.Game.Collections
public Scroll()
{
- ScrollbarVisible = false;
- Padding = new MarginPadding(10);
+ ScrollbarOverlapsContent = false;
base.Content.Add(new FillFlowContainer
{
@@ -133,7 +168,7 @@ namespace osu.Game.Collections
base.Update();
// AutoSizeAxes cannot be used as the height should represent the post-layout-transform height at all times, so that the placeholder doesn't bounce around.
- content.Height = ((Flow)Child).Children.Sum(c => c.DrawHeight + 5);
+ content.Height = ((Flow)Child).Children.Sum(c => c.IsPresent ? c.DrawHeight + 5 : 0);
}
///
@@ -179,7 +214,7 @@ namespace osu.Game.Collections
///
/// The flow of . Disables layout easing unless a drag is in progress.
///
- private partial class Flow : FillFlowContainer>>
+ private partial class Flow : SearchContainer>>
{
public readonly IBindable DragActive = new Bindable();
@@ -187,6 +222,8 @@ namespace osu.Game.Collections
{
Spacing = new Vector2(0, 5);
LayoutEasing = Easing.OutQuint;
+
+ Padding = new MarginPadding { Right = 5 };
}
protected override void LoadComplete()
diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs
index f07ec87353..e86254329f 100644
--- a/osu.Game/Collections/DrawableCollectionListItem.cs
+++ b/osu.Game/Collections/DrawableCollectionListItem.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -10,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Localisation;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@@ -23,7 +25,7 @@ namespace osu.Game.Collections
///
/// Visualises a inside a .
///
- public partial class DrawableCollectionListItem : OsuRearrangeableListItem>
+ public partial class DrawableCollectionListItem : OsuRearrangeableListItem>, IFilterable
{
private const float item_height = 35;
private const float button_width = item_height * 0.75f;
@@ -207,5 +209,25 @@ namespace osu.Game.Collections
private void deleteCollection() => collection.PerformWrite(c => c.Realm!.Remove(c));
}
+
+ public IEnumerable FilterTerms => [(LocalisableString)Model.Value.Name];
+
+ private bool matchingFilter = true;
+
+ public bool MatchingFilter
+ {
+ get => matchingFilter;
+ set
+ {
+ matchingFilter = value;
+
+ if (matchingFilter)
+ this.FadeIn(200);
+ else
+ Hide();
+ }
+ }
+
+ public bool FilteringActive { get; set; }
}
}
diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs
index 9f8158af53..a738ae66cb 100644
--- a/osu.Game/Collections/ManageCollectionsDialog.cs
+++ b/osu.Game/Collections/ManageCollectionsDialog.cs
@@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
+using osu.Game.Resources.Localisation.Web;
using osuTK;
namespace osu.Game.Collections
@@ -26,6 +27,9 @@ namespace osu.Game.Collections
private IDisposable? duckOperation;
+ private BasicSearchTextBox searchTextBox = null!;
+ private DrawableCollectionList list = null!;
+
[Resolved]
private MusicController? musicController { get; set; }
@@ -104,10 +108,31 @@ namespace osu.Game.Collections
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeaFoamDarker
},
- new DrawableCollectionList
+ new Container
{
RelativeSizeAxes = Axes.Both,
- }
+ Padding = new MarginPadding(10),
+ Children = new Drawable[]
+ {
+ searchTextBox = new BasicSearchTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ Y = 10,
+ Height = 40,
+ ReleaseFocusOnCommit = false,
+ HoldFocus = true,
+ PlaceholderText = HomeStrings.SearchPlaceholder,
+ },
+ list = new DrawableCollectionList
+ {
+ Padding = new MarginPadding
+ {
+ Top = 60,
+ },
+ RelativeSizeAxes = Axes.Both,
+ }
+ }
+ },
}
}
},
@@ -117,6 +142,16 @@ namespace osu.Game.Collections
};
}
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ searchTextBox.Current.BindValueChanged(_ =>
+ {
+ list.SearchTerm = searchTextBox.Current.Value;
+ });
+ }
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index af6fd61a3d..362c06849d 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential);
SetDefault(OsuSetting.ModSelectTextSearchStartsActive, true);
- SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
+ SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f, 0.01f);
SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal);
@@ -132,7 +132,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.Prefer24HourTime, !CultureInfoHelper.SystemCulture.DateTimeFormat.ShortTimePattern.Contains(@"tt"));
// Gameplay
- SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1);
+ SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1, 0.01f);
SetDefault(OsuSetting.DimLevel, 0.7, 0, 1, 0.01);
SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
SetDefault(OsuSetting.LightenDuringBreaks, true);
@@ -169,13 +169,13 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.Scaling, ScalingMode.Off);
SetDefault(OsuSetting.SafeAreaConsiderations, true);
- SetDefault(OsuSetting.ScalingBackgroundDim, 0.9f, 0.5f, 1f);
+ SetDefault(OsuSetting.ScalingBackgroundDim, 0.9f, 0.5f, 1f, 0.01f);
- SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
- SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
+ SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f, 0.01f);
+ SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f, 0.01f);
- SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
- SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
+ SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f, 0.01f);
+ SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f, 0.01f);
SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs
index 8aece748a8..dfeec259fe 100644
--- a/osu.Game/Database/ModelDownloader.cs
+++ b/osu.Game/Database/ModelDownloader.cs
@@ -68,18 +68,23 @@ namespace osu.Game.Database
{
Task.Factory.StartNew(async () =>
{
- bool importSuccessful;
+ bool importSuccessful = false;
- if (originalModel != null)
- importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel).ConfigureAwait(false)) != null;
- else
- importSuccessful = (await importer.Import(notification, new[] { new ImportTask(filename) }).ConfigureAwait(false)).Any();
+ try
+ {
+ if (originalModel != null)
+ importSuccessful = (await importer.ImportAsUpdate(notification, new ImportTask(filename), originalModel).ConfigureAwait(false)) != null;
+ else
+ importSuccessful = (await importer.Import(notification, new[] { new ImportTask(filename) }).ConfigureAwait(false)).Any();
+ }
+ finally
+ {
+ // for now a failed import will be marked as a failed download for simplicity.
+ if (!importSuccessful)
+ DownloadFailed?.Invoke(request);
- // for now a failed import will be marked as a failed download for simplicity.
- if (!importSuccessful)
- DownloadFailed?.Invoke(request);
-
- CurrentDownloads.Remove(request);
+ CurrentDownloads.Remove(request);
+ }
}, TaskCreationOptions.LongRunning);
};
diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs
index 75462797f8..e538530b79 100644
--- a/osu.Game/Database/RealmArchiveModelImporter.cs
+++ b/osu.Game/Database/RealmArchiveModelImporter.cs
@@ -105,7 +105,6 @@ namespace osu.Game.Database
}
notification.Progress = 0;
- notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
int current = 0;
@@ -113,65 +112,78 @@ namespace osu.Game.Database
parameters.Batch |= tasks.Length >= minimum_items_considered_batch_import;
- await Task.WhenAll(tasks.Select(async task =>
+ notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
+ notification.State = ProgressNotificationState.Active;
+
+ await pauseIfNecessaryAsync(parameters, notification, notification.CancellationToken).ConfigureAwait(false);
+
+ try
{
- if (notification.CancellationToken.IsCancellationRequested)
- return;
-
- try
+ await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) =>
{
- var model = await Import(task, parameters, notification.CancellationToken).ConfigureAwait(false);
+ cancellation.ThrowIfCancellationRequested();
- lock (imported)
+ try
{
- if (model != null)
- imported.Add(model);
- current++;
+ await pauseIfNecessaryAsync(parameters, notification, cancellation).ConfigureAwait(false);
- notification.Text = $"Imported {current} of {tasks.Length} {HumanisedModelName}s";
- notification.Progress = (float)current / tasks.Length;
+ var model = await Import(task, parameters, cancellation).ConfigureAwait(false);
+
+ lock (imported)
+ {
+ if (model != null)
+ imported.Add(model);
+ current++;
+
+ notification.Text = $"Imported {current} of {tasks.Length} {HumanisedModelName}s";
+ notification.Progress = (float)current / tasks.Length;
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database);
+ }
+ }).ConfigureAwait(false);
+ }
+ finally
+ {
+ if (imported.Count == 0)
+ {
+ if (notification.CancellationToken.IsCancellationRequested)
+ {
+ notification.State = ProgressNotificationState.Cancelled;
+ }
+ else
+ {
+ notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import failed! Check logs for more information.";
+ notification.State = ProgressNotificationState.Cancelled;
}
}
- catch (OperationCanceledException)
- {
- }
- catch (Exception e)
- {
- Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database);
- }
- })).ConfigureAwait(false);
-
- if (imported.Count == 0)
- {
- if (notification.CancellationToken.IsCancellationRequested)
- {
- notification.State = ProgressNotificationState.Cancelled;
- return imported;
- }
-
- notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import failed! Check logs for more information.";
- notification.State = ProgressNotificationState.Cancelled;
- }
- else
- {
- if (tasks.Length > imported.Count)
- notification.CompletionText = $"Imported {imported.Count} of {tasks.Length} {HumanisedModelName}s.";
- else if (imported.Count > 1)
- notification.CompletionText = $"Imported {imported.Count} {HumanisedModelName}s!";
else
- notification.CompletionText = $"Imported {imported.First().GetDisplayString()}!";
-
- if (imported.Count > 0 && PresentImport != null)
{
- notification.CompletionText += " Click to view.";
- notification.CompletionClickAction = () =>
- {
- PresentImport?.Invoke(imported);
- return true;
- };
- }
+ if (tasks.Length > imported.Count)
+ notification.CompletionText = $"Imported {imported.Count} of {tasks.Length} {HumanisedModelName}s.";
+ else if (imported.Count > 1)
+ notification.CompletionText = $"Imported {imported.Count} {HumanisedModelName}s!";
+ else
+ notification.CompletionText = $"Imported {imported.First().GetDisplayString()}!";
- notification.State = ProgressNotificationState.Completed;
+ if (imported.Count > 0 && PresentImport != null)
+ {
+ notification.CompletionText += " Click to view.";
+ notification.CompletionClickAction = () =>
+ {
+ PresentImport?.Invoke(imported);
+ return true;
+ };
+ }
+
+ notification.State = ProgressNotificationState.Completed;
+ }
}
return imported;
@@ -286,8 +298,6 @@ namespace osu.Game.Database
/// An optional cancellation token.
public virtual Live? ImportModel(TModel item, ArchiveReader? archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => Realm.Run(realm =>
{
- pauseIfNecessary(parameters, cancellationToken);
-
TModel? existing;
if (parameters.Batch && archive != null)
@@ -528,7 +538,8 @@ namespace osu.Game.Database
/// The new model proposed for import.
/// The current realm context.
/// An existing model which matches the criteria to skip importing, else null.
- protected TModel? CheckForExisting(TModel model, Realm realm) => string.IsNullOrEmpty(model.Hash) ? null : realm.All().OrderBy(b => b.DeletePending).FirstOrDefault(b => b.Hash == model.Hash);
+ protected TModel? CheckForExisting(TModel model, Realm realm) =>
+ string.IsNullOrEmpty(model.Hash) ? null : realm.All().OrderBy(b => b.DeletePending).FirstOrDefault(b => b.Hash == model.Hash);
///
/// Whether import can be skipped after finding an existing import early in the process.
@@ -575,21 +586,29 @@ namespace osu.Game.Database
/// Whether to perform deletion.
protected virtual bool ShouldDeleteArchive(string path) => false;
- private void pauseIfNecessary(ImportParameters importParameters, CancellationToken cancellationToken)
+ private async Task pauseIfNecessaryAsync(ImportParameters importParameters, ProgressNotification notification, CancellationToken cancellationToken)
{
if (!PauseImports || importParameters.ImportImmediately)
return;
Logger.Log($@"{GetType().Name} is being paused.");
+ // A paused state could obviously be entered mid-import (during the `Task.WhenAll` below),
+ // but in order to keep things simple let's focus on the most common scenario.
+ notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is paused due to gameplay...";
+ notification.State = ProgressNotificationState.Queued;
+
while (PauseImports)
{
cancellationToken.ThrowIfCancellationRequested();
- Thread.Sleep(500);
+ await Task.Delay(500, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
Logger.Log($@"{GetType().Name} is being resumed.");
+
+ notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is resuming...";
+ notification.State = ProgressNotificationState.Active;
}
private IEnumerable getIDs(IEnumerable files)
diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index ffd28957ef..a3cd5a4902 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -42,8 +42,6 @@ namespace osu.Game.Graphics.Containers
///
public double DistanceDecayOnRightMouseScrollbar = 0.02;
- private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right;
-
private bool rightMouseDragging;
protected override bool IsDragging => base.IsDragging || rightMouseDragging;
@@ -126,8 +124,15 @@ namespace osu.Game.Graphics.Containers
return base.OnScroll(e);
}
- protected virtual void ScrollFromMouseEvent(MouseEvent e) =>
- ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim] * Content.DrawSize[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar);
+ protected virtual void ScrollFromMouseEvent(MouseEvent e)
+ {
+ float fromScrollbarPosition = FromScrollbarPosition(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim]);
+ float scrollbarCentreOffset = FromScrollbarPosition(Scrollbar.DrawHeight) * 0.5f;
+
+ ScrollTo(Clamp(fromScrollbarPosition - scrollbarCentreOffset), true, DistanceDecayOnRightMouseScrollbar);
+ }
+
+ private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right;
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction);
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..7feb709acb 100644
--- a/osu.Game/Online/Rooms/GetRoomsRequest.cs
+++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs
@@ -1,12 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System;
using System.Collections.Generic;
using osu.Framework.IO.Network;
using osu.Game.Extensions;
using osu.Game.Online.API;
-using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Online.Rooms
@@ -35,25 +33,6 @@ namespace osu.Game.Online.Rooms
return req;
}
- protected override void PostProcess()
- {
- base.PostProcess();
-
- if (Response != null)
- {
- // 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();
- else
- room.Status.Value = new RoomStatusOpen();
- }
- }
- }
-
protected override string Target => "rooms";
}
}
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..486f70c0ed 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -1,13 +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.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
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 +16,344 @@ 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);
+ }
+
+ [OnDeserialized]
+ private void onDeserialised(StreamingContext context)
+ {
+ // API doesn't populate status so let's do it here.
+ if (EndDate != null && DateTimeOffset.Now >= EndDate)
+ Status = new RoomStatusEnded();
+ else if (HasPassword)
+ Status = new RoomStatusOpenPrivate();
+ else
+ Status = new RoomStatusOpen();
+ }
+
+ [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 +364,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 +399,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 +413,7 @@ namespace osu.Game.Online.Rooms
public int CountTotal;
[JsonProperty("ruleset_ids")]
- public int[] RulesetIDs;
+ public int[] RulesetIDs = [];
}
[JsonObject(MemberSerialization.OptIn)]
@@ -252,5 +425,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/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs
index 42908f7102..d0ee2ccd71 100644
--- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs
+++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs
@@ -34,11 +34,12 @@ using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Skinning;
using osu.Framework.Graphics.Cursor;
+using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.SkinEditor
{
[Cached(typeof(SkinEditor))]
- public partial class SkinEditor : VisibilityContainer, ICanAcceptFiles, IKeyBindingHandler, IEditorChangeHandler
+ public partial class SkinEditor : VisibilityContainer, ICanAcceptFiles, IKeyBindingHandler, IKeyBindingHandler, IEditorChangeHandler
{
public const double TRANSITION_DURATION = 300;
@@ -155,7 +156,7 @@ namespace osu.Game.Overlays.SkinEditor
{
Items = new OsuMenuItem[]
{
- new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
+ new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()) { Hotkey = new Hotkey(PlatformAction.Save) },
new EditorMenuItem(CommonStrings.Export, MenuItemType.Standard, () => skins.ExportCurrentSkin()) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
new OsuMenuItemSpacer(),
new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))),
@@ -167,13 +168,13 @@ namespace osu.Game.Overlays.SkinEditor
{
Items = new OsuMenuItem[]
{
- undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo),
- redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo),
+ undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo) { Hotkey = new Hotkey(PlatformAction.Undo) },
+ redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo) { Hotkey = new Hotkey(PlatformAction.Redo) },
new OsuMenuItemSpacer(),
- cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut),
- copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy),
- pasteMenuItem = new EditorMenuItem(CommonStrings.Paste, MenuItemType.Standard, Paste),
- cloneMenuItem = new EditorMenuItem(CommonStrings.Clone, MenuItemType.Standard, Clone),
+ cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut) { Hotkey = new Hotkey(PlatformAction.Cut) },
+ copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy) { Hotkey = new Hotkey(PlatformAction.Copy) },
+ pasteMenuItem = new EditorMenuItem(CommonStrings.Paste, MenuItemType.Standard, Paste) { Hotkey = new Hotkey(PlatformAction.Paste) },
+ cloneMenuItem = new EditorMenuItem(CommonStrings.Clone, MenuItemType.Standard, Clone) { Hotkey = new Hotkey(GlobalAction.EditorCloneSelection) },
}
},
}
@@ -313,6 +314,25 @@ namespace osu.Game.Overlays.SkinEditor
{
}
+ public bool OnPressed(KeyBindingPressEvent e)
+ {
+ if (e.Repeat)
+ return false;
+
+ switch (e.Action)
+ {
+ case GlobalAction.EditorCloneSelection:
+ Clone();
+ return true;
+ }
+
+ return false;
+ }
+
+ public void OnReleased(KeyBindingReleaseEvent e)
+ {
+ }
+
public void UpdateTargetScreen(Drawable targetScreen)
{
this.targetScreen = targetScreen;
diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
index cf41c8e108..5bf15aee8b 100644
--- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
+++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs
@@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Edit
return (lastBefore, firstAfter);
}
- protected abstract double ReadCurrentDistanceSnap(HitObject before, HitObject after);
+ public abstract double ReadCurrentDistanceSnap(HitObject before, HitObject after);
protected override void Update()
{
diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
index a9dbfc29a9..6724a1dc4d 100644
--- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs
@@ -258,8 +258,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void resetTernaryStates()
{
- if (SelectionNewComboState.Value == TernaryState.Indeterminate)
- SelectionNewComboState.Value = TernaryState.False;
+ SelectionNewComboState.Value = TernaryState.False;
AutoSelectionBankEnabled.Value = true;
SelectionAdditionBanksEnabled.Value = true;
SelectionBankStates[HIT_BANK_AUTO].Value = TernaryState.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..b10ce8ed1b 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;
@@ -20,7 +18,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
public IBindable InitialRoomsReceived => initialRoomsReceived;
private readonly Bindable initialRoomsReceived = new Bindable();
- public readonly Bindable Filter = new Bindable();
+ public readonly Bindable Filter = new Bindable();
[BackgroundDependencyLoader]
private void load()
@@ -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/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs
index 5a1648c91f..207e0bdf55 100644
--- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs
+++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.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 osu.Framework.Bindables;
@@ -26,22 +24,22 @@ namespace osu.Game.Screens.OnlinePlay
/// The currently-selected item. Selection is visually represented with a border.
/// May be updated by clicking playlist items if is true.
///
- public readonly Bindable SelectedItem = new Bindable();
+ public readonly Bindable SelectedItem = new Bindable();
///
/// Invoked when an item is requested to be deleted.
///
- public Action RequestDeletion;
+ public Action? RequestDeletion;
///
/// Invoked when an item requests its results to be shown.
///
- public Action RequestResults;
+ public Action? RequestResults;
///
/// Invoked when an item requests to be edited.
///
- public Action RequestEdit;
+ public Action? RequestEdit;
private bool allowReordering;
@@ -235,7 +233,7 @@ namespace osu.Game.Screens.OnlinePlay
{
var visibleItems = ListContainer.AsEnumerable().Where(r => r.IsPresent);
- PlaylistItem item;
+ PlaylistItem? item;
if (SelectedItem.Value == null)
item = visibleItems.FirstOrDefault()?.Model;
diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs
index 43ffaf947e..7a773bb116 100644
--- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs
+++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.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;
@@ -54,23 +52,23 @@ namespace osu.Game.Screens.OnlinePlay
///
/// Invoked when this item requests to be deleted.
///
- public Action RequestDeletion;
+ public Action? RequestDeletion;
///
/// Invoked when this item requests its results to be shown.
///
- public Action RequestResults;
+ public Action? RequestResults;
///
/// Invoked when this item requests to be edited.
///
- public Action RequestEdit;
+ public Action? RequestEdit;
///
/// The currently-selected item, used to show a border around this item.
/// May be updated by this item if is true.
///
- public readonly Bindable SelectedItem = new Bindable();
+ public readonly Bindable SelectedItem = new Bindable();
public readonly PlaylistItem Item;
@@ -79,48 +77,48 @@ namespace osu.Game.Screens.OnlinePlay
private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both };
private readonly IBindable valid = new Bindable();
- private IBeatmapInfo beatmap;
- private IRulesetInfo ruleset;
+ private IBeatmapInfo? beatmap;
+ private IRulesetInfo? ruleset;
private Mod[] requiredMods = Array.Empty();
- private Container borderContainer;
- private FillFlowContainer difficultyIconContainer;
- private LinkFlowContainer beatmapText;
- private LinkFlowContainer authorText;
- private ExplicitContentBeatmapBadge explicitContent;
- private ModDisplay modDisplay;
- private FillFlowContainer buttonsFlow;
- private UpdateableAvatar ownerAvatar;
- private Drawable showResultsButton;
- private Drawable editButton;
- private Drawable removeButton;
- private PanelBackground panelBackground;
- private FillFlowContainer mainFillFlow;
- private BeatmapCardThumbnail thumbnail;
+ private Container? borderContainer;
+ private FillFlowContainer? difficultyIconContainer;
+ private LinkFlowContainer? beatmapText;
+ private LinkFlowContainer? authorText;
+ private ExplicitContentBeatmapBadge? explicitContent;
+ private ModDisplay? modDisplay;
+ private FillFlowContainer? buttonsFlow;
+ private UpdateableAvatar? ownerAvatar;
+ private Drawable? showResultsButton;
+ private Drawable? editButton;
+ private Drawable? removeButton;
+ private PanelBackground? panelBackground;
+ private FillFlowContainer? mainFillFlow;
+ private BeatmapCardThumbnail? thumbnail;
[Resolved]
- private RealmAccess realm { get; set; }
+ private RealmAccess realm { get; set; } = null!;
[Resolved]
- private RulesetStore rulesets { get; set; }
+ private RulesetStore rulesets { get; set; } = null!;
[Resolved]
- private BeatmapManager beatmaps { get; set; }
+ private BeatmapManager beatmaps { get; set; } = null!;
[Resolved]
- private OsuColour colours { get; set; }
+ private OsuColour colours { get; set; } = null!;
[Resolved]
- private UserLookupCache userLookupCache { get; set; }
+ private UserLookupCache userLookupCache { get; set; } = null!;
[Resolved]
- private BeatmapLookupCache beatmapLookupCache { get; set; }
+ private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
[Resolved(CanBeNull = true)]
- private BeatmapSetOverlay beatmapOverlay { get; set; }
+ private BeatmapSetOverlay? beatmapOverlay { get; set; }
[Resolved(CanBeNull = true)]
- private ManageCollectionsDialog manageCollectionsDialog { get; set; }
+ private ManageCollectionsDialog? manageCollectionsDialog { get; set; }
public DrawableRoomPlaylistItem(PlaylistItem item)
: base(item)
@@ -136,7 +134,8 @@ namespace osu.Game.Screens.OnlinePlay
[BackgroundDependencyLoader]
private void load()
{
- borderContainer.BorderColour = colours.Yellow;
+ if (borderContainer != null)
+ borderContainer.BorderColour = colours.Yellow;
ruleset = rulesets.GetRuleset(Item.RulesetID);
var rulesetInstance = ruleset?.CreateInstance();
@@ -163,7 +162,8 @@ namespace osu.Game.Screens.OnlinePlay
return;
}
- borderContainer.BorderThickness = IsSelectedItem ? border_thickness : 0;
+ if (borderContainer != null)
+ borderContainer.BorderThickness = IsSelectedItem ? border_thickness : 0;
}, true);
valid.BindValueChanged(_ => Scheduler.AddOnce(refresh));
@@ -177,7 +177,11 @@ namespace osu.Game.Screens.OnlinePlay
if (showItemOwner)
{
var foundUser = await userLookupCache.GetUserAsync(Item.OwnerID).ConfigureAwait(false);
- Schedule(() => ownerAvatar.User = foundUser);
+ Schedule(() =>
+ {
+ if (ownerAvatar != null)
+ ownerAvatar.User = foundUser;
+ });
}
beatmap = await beatmapLookupCache.GetBeatmapAsync(Item.Beatmap.OnlineID).ConfigureAwait(false);
@@ -278,69 +282,89 @@ namespace osu.Game.Screens.OnlinePlay
private void refresh()
{
- if (!valid.Value)
+ if (borderContainer != null)
{
- borderContainer.BorderThickness = border_thickness;
- borderContainer.BorderColour = colours.Red;
- }
-
- if (beatmap != null)
- {
- difficultyIconContainer.Children = new Drawable[]
+ if (!valid.Value)
{
- thumbnail = new BeatmapCardThumbnail(beatmap.BeatmapSet!, (IBeatmapSetOnlineInfo)beatmap.BeatmapSet!)
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Width = 60,
- Masking = true,
- CornerRadius = 10,
- RelativeSizeAxes = Axes.Y,
- Dimmed = { Value = IsHovered }
- },
- new DifficultyIcon(beatmap, ruleset, requiredMods)
- {
- Size = new Vector2(24),
- TooltipType = DifficultyIconTooltipType.Extended,
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- },
- };
+ borderContainer.BorderThickness = border_thickness;
+ borderContainer.BorderColour = colours.Red;
+ }
}
- else
- difficultyIconContainer.Clear();
- panelBackground.Beatmap.Value = beatmap;
-
- beatmapText.Clear();
-
- if (beatmap != null)
+ if (difficultyIconContainer != null)
{
- beatmapText.AddLink(beatmap.GetDisplayTitleRomanisable(includeCreator: false),
- LinkAction.OpenBeatmap,
- beatmap.OnlineID.ToString(),
- null,
- text =>
+ if (beatmap != null)
+ {
+ difficultyIconContainer.Children = new Drawable[]
{
- text.Truncate = true;
- });
+ thumbnail = new BeatmapCardThumbnail(beatmap.BeatmapSet!, (IBeatmapSetOnlineInfo)beatmap.BeatmapSet!)
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 60,
+ Masking = true,
+ CornerRadius = 10,
+ RelativeSizeAxes = Axes.Y,
+ Dimmed = { Value = IsHovered }
+ },
+ new DifficultyIcon(beatmap, ruleset, requiredMods)
+ {
+ Size = new Vector2(24),
+ TooltipType = DifficultyIconTooltipType.Extended,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ },
+ };
+ }
+ else
+ difficultyIconContainer.Clear();
}
- authorText.Clear();
+ if (panelBackground != null)
+ panelBackground.Beatmap.Value = beatmap;
- if (!string.IsNullOrEmpty(beatmap?.Metadata.Author.Username))
+ if (beatmapText != null)
{
- authorText.AddText("mapped by ");
- authorText.AddUserLink(beatmap.Metadata.Author);
+ beatmapText.Clear();
+
+ if (beatmap != null)
+ {
+ beatmapText.AddLink(beatmap.GetDisplayTitleRomanisable(includeCreator: false),
+ LinkAction.OpenBeatmap,
+ beatmap.OnlineID.ToString(),
+ null,
+ text =>
+ {
+ text.Truncate = true;
+ });
+ }
}
- bool hasExplicitContent = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
- explicitContent.Alpha = hasExplicitContent ? 1 : 0;
+ if (authorText != null)
+ {
+ authorText.Clear();
- modDisplay.Current.Value = requiredMods.ToArray();
+ if (!string.IsNullOrEmpty(beatmap?.Metadata.Author.Username))
+ {
+ authorText.AddText("mapped by ");
+ authorText.AddUserLink(beatmap.Metadata.Author);
+ }
+ }
- buttonsFlow.Clear();
- buttonsFlow.ChildrenEnumerable = createButtons();
+ if (explicitContent != null)
+ {
+ bool hasExplicitContent = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true;
+ explicitContent.Alpha = hasExplicitContent ? 1 : 0;
+ }
+
+ if (modDisplay != null)
+ modDisplay.Current.Value = requiredMods.ToArray();
+
+ if (buttonsFlow != null)
+ {
+ buttonsFlow.Clear();
+ buttonsFlow.ChildrenEnumerable = createButtons();
+ }
difficultyIconContainer.FadeInFromZero(500, Easing.OutQuint);
mainFillFlow.FadeInFromZero(500, Easing.OutQuint);
@@ -601,7 +625,7 @@ namespace osu.Game.Screens.OnlinePlay
private readonly IBeatmapInfo beatmap;
[Resolved]
- private BeatmapManager beatmapManager { get; set; }
+ private BeatmapManager beatmapManager { get; set; } = null!;
// required for download tracking, as this button hides itself. can probably be removed with a bit of consideration.
public override bool IsPresent => true;
@@ -656,7 +680,7 @@ namespace osu.Game.Screens.OnlinePlay
// For now, this is the same implementation as in PanelBackground, but supports a beatmap info rather than a working beatmap
private partial class PanelBackground : Container // todo: should be a buffered container (https://github.com/ppy/osu-framework/issues/3222)
{
- public readonly Bindable Beatmap = new Bindable();
+ public readonly Bindable Beatmap = new Bindable();
public PanelBackground()
{
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/FilterCriteria.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs
index 3a687ad351..0f63718355 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs
@@ -1,18 +1,16 @@
// 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.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public class FilterCriteria
{
- public string SearchString;
+ public string SearchString = string.Empty;
public RoomStatusFilter Status;
- public string Category;
- public RulesetInfo Ruleset;
+ public string Category = string.Empty;
+ public RulesetInfo? Ruleset;
public RoomPermissionsFilter Permissions;
}
}
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..ac8caa6b88 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.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.Extensions;
@@ -54,42 +51,42 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
AutoSizeAxes = Axes.Both
};
- protected ListingPollingComponent ListingPollingComponent { get; private set; }
+ protected ListingPollingComponent ListingPollingComponent { get; private set; } = null!;
- protected readonly Bindable SelectedRoom = new Bindable();
+ protected readonly Bindable SelectedRoom = new Bindable();
[Resolved]
- private MusicController music { get; set; }
+ private MusicController music { get; set; } = null!;
[Resolved(CanBeNull = true)]
- private OngoingOperationTracker ongoingOperationTracker { get; set; }
+ private OngoingOperationTracker? ongoingOperationTracker { get; set; }
[Resolved]
- private IBindable ruleset { get; set; }
+ private IBindable ruleset { get; set; } = null!;
[Resolved]
- private IAPIProvider api { get; set; }
+ private IAPIProvider api { get; set; } = null!;
- [CanBeNull]
- private IDisposable joiningRoomOperation { get; set; }
-
- [CanBeNull]
- private LeasedBindable selectionLease;
+ [Resolved(CanBeNull = true)]
+ private IdleTracker? idleTracker { get; set; }
[Resolved]
- protected OsuConfigManager Config { get; private set; }
+ protected OsuConfigManager Config { get; private set; } = null!;
- private readonly Bindable filter = new Bindable(new FilterCriteria());
+ private IDisposable? joiningRoomOperation { get; set; }
+ private LeasedBindable? selectionLease;
+
+ private readonly Bindable filter = new Bindable();
private readonly IBindable operationInProgress = new Bindable();
private readonly IBindable isIdle = new BindableBool();
- private PopoverContainer popoverContainer;
- private LoadingLayer loadingLayer;
- private RoomsContainer roomsContainer;
- private SearchTextBox searchTextBox;
- private Dropdown statusDropdown;
+ private PopoverContainer popoverContainer = null!;
+ private LoadingLayer loadingLayer = null!;
+ private RoomsContainer roomsContainer = null!;
+ private SearchTextBox searchTextBox = null!;
+ private Dropdown statusDropdown = null!;
[BackgroundDependencyLoader(true)]
- private void load([CanBeNull] IdleTracker idleTracker)
+ private void load()
{
const float controls_area_height = 25f;
@@ -208,7 +205,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public void UpdateFilter() => Scheduler.AddOnce(updateFilter);
- private ScheduledDelegate scheduledFilterUpdate;
+ private ScheduledDelegate? scheduledFilterUpdate;
private void updateFilterDebounced()
{
@@ -259,10 +256,10 @@ 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();
+ music.EnsurePlayingSomething();
onReturning();
}
@@ -299,7 +296,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
popoverContainer.HidePopover();
}
- public virtual void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() =>
+ public virtual void Join(Room room, string? password, Action? onSuccess = null, Action? onFailure = null) => Schedule(() =>
{
if (joiningRoomOperation != null)
return;
@@ -326,23 +323,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);
@@ -364,7 +361,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
/// Push a room as a new subscreen.
///
/// An optional template to use when creating the room.
- public void Open(Room room = null) => Schedule(() =>
+ public void Open(Room? room = null) => Schedule(() =>
{
// Handles the case where a room is clicked 3 times in quick succession
if (!this.IsCurrentScreen())
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/MatchLeaderboardScore.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs
index fabebc3859..2ea0f9eb84 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchLeaderboardScore.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.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
@@ -17,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{
private readonly APIUserScoreAggregate score;
- public override ScoreInfo TooltipContent => null; // match aggregate scores can't show statistics that the custom tooltip displays.
+ public override ScoreInfo? TooltipContent => null; // match aggregate scores can't show statistics that the custom tooltip displays.
public MatchLeaderboardScore(APIUserScoreAggregate score, int? rank, bool isOnlineScope = true)
: base(score.CreateScoreInfo(), rank, isOnlineScope)
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs
index 477336e8ea..51bd7a2801 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchTypePicker.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.Audio;
using osu.Framework.Audio.Sample;
@@ -26,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected override TabItem CreateTabItem(MatchType value) => new GameTypePickerItem(value);
- protected override Dropdown CreateDropdown() => null;
+ protected override Dropdown? CreateDropdown() => null;
public MatchTypePicker()
{
@@ -41,7 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{
private const float transition_duration = 200;
- private readonly CircularContainer hover, selection;
+ private readonly CircularContainer hover;
+ private readonly CircularContainer selection;
public GameTypePickerItem(MatchType value)
: base(value)
@@ -84,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
};
}
- private Sample selectSample;
+ private Sample selectSample = null!;
[BackgroundDependencyLoader]
private void load(OsuColour colours, AudioManager audio)
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs
index 85fac9228b..56f02ba633 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomAvailabilityPicker.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.Extensions;
using osu.Framework.Extensions.Color4Extensions;
@@ -22,7 +20,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
public partial class RoomAvailabilityPicker : DisableableTabControl
{
protected override TabItem CreateTabItem(RoomAvailability value) => new RoomAvailabilityPickerItem(value);
- protected override Dropdown CreateDropdown() => null;
+ protected override Dropdown? CreateDropdown() => null;
public RoomAvailabilityPicker()
{
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/RoomModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/RoomModSelectOverlay.cs
index 6a856d8d72..ffa4235167 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomModSelectOverlay.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomModSelectOverlay.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
{
public partial class RoomModSelectOverlay : UserModSelectOverlay
{
- public Bindable SelectedItem { get; } = new Bindable();
+ public Bindable SelectedItem { get; } = new Bindable();
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
index 7c8931c04e..ffea3878fa 100644
--- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
@@ -1,13 +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;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
-using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@@ -29,14 +27,14 @@ 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 readonly Bindable SelectedItem = new Bindable