From d4bfbe59174a7669d6027d704092ff829cb990d5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 29 Aug 2024 20:21:32 +0900 Subject: [PATCH 01/73] Add test --- .../ManiaScoreProcessorTest.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs new file mode 100644 index 0000000000..1516aaf058 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaScoreProcessorTest.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaScoreProcessorTest + { + [TestCase(ScoreRank.X, 1, HitResult.Perfect)] + [TestCase(ScoreRank.X, 0.99, HitResult.Great)] + [TestCase(ScoreRank.D, 0.1, HitResult.Great)] + [TestCase(ScoreRank.X, 0.99, HitResult.Perfect, HitResult.Great)] + [TestCase(ScoreRank.X, 0.99, HitResult.Great, HitResult.Great)] + [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Good)] + [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Ok)] + [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Meh)] + [TestCase(ScoreRank.S, 0.99, HitResult.Perfect, HitResult.Miss)] + [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Good)] + [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Ok)] + [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Meh)] + [TestCase(ScoreRank.S, 0.99, HitResult.Great, HitResult.Miss)] + public void TestRanks(ScoreRank expected, double accuracy, params HitResult[] results) + { + var scoreProcessor = new ManiaScoreProcessor(); + + Dictionary resultsDict = new Dictionary(); + foreach (var result in results) + resultsDict[result] = resultsDict.GetValueOrDefault(result) + 1; + + Assert.That(scoreProcessor.RankFromScore(accuracy, resultsDict), Is.EqualTo(expected)); + } + } +} From d59d5685d08ef99f15beba32e60f2ebe0c2b9c18 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 29 Aug 2024 20:21:59 +0900 Subject: [PATCH 02/73] Make mania award SS even if there are GREAT judgements --- .../Scoring/ManiaScoreProcessor.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 0444394d87..dfd6ed6dd2 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Mania.Scoring { @@ -58,6 +59,24 @@ namespace osu.Game.Rulesets.Mania.Scoring return GetBaseScoreForResult(result); } + public override ScoreRank RankFromScore(double accuracy, IReadOnlyDictionary results) + { + ScoreRank rank = base.RankFromScore(accuracy, results); + + if (rank != ScoreRank.S) + return rank; + + // SS is expected as long as all hitobjects have been hit with either a GREAT or PERFECT result. + + bool anyImperfect = + results.GetValueOrDefault(HitResult.Good) > 0 + || results.GetValueOrDefault(HitResult.Ok) > 0 + || results.GetValueOrDefault(HitResult.Meh) > 0 + || results.GetValueOrDefault(HitResult.Miss) > 0; + + return anyImperfect ? rank : ScoreRank.X; + } + private class JudgementOrderComparer : IComparer { public static readonly JudgementOrderComparer DEFAULT = new JudgementOrderComparer(); From d97a94aadd68aada70a8d808fdb7622e96267e44 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 29 Aug 2024 20:22:54 +0900 Subject: [PATCH 03/73] Change accuracy circle to show badges based on score rank --- osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index cebc54f490..319a87fdfc 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -298,7 +298,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy { foreach (var badge in badges) { - if (badge.Accuracy > score.Accuracy) + if (badge.Rank > score.Rank) continue; using (BeginDelayedSequence( From be5cb209e502c1df833ad7dc542c6fc4a1c8a846 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Oct 2024 17:51:29 +0900 Subject: [PATCH 04/73] Update notification text when import is paused due to gameplay Addresses https://github.com/ppy/osu/discussions/30388. --- osu.Game/Database/RealmArchiveModelImporter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index cf0625c51c..a27e35fc4e 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,6 +112,18 @@ namespace osu.Game.Database parameters.Batch |= tasks.Length >= minimum_items_considered_batch_import; + // 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..."; + + try + { + pauseIfNecessary(parameters, notification.CancellationToken); + } + catch { } + + notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising..."; + await Task.WhenAll(tasks.Select(async task => { if (notification.CancellationToken.IsCancellationRequested) From 61f0cfd688f45d8bc02ca24a71566ed662aa8f56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Oct 2024 17:56:51 +0900 Subject: [PATCH 05/73] Make flow more `async` to avoid any chance of deadlocks --- .../Database/RealmArchiveModelImporter.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index a27e35fc4e..200c2051fc 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -112,26 +112,20 @@ namespace osu.Game.Database parameters.Batch |= tasks.Length >= minimum_items_considered_batch_import; - // 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..."; - - try - { - pauseIfNecessary(parameters, notification.CancellationToken); - } - catch { } - notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising..."; + notification.State = ProgressNotificationState.Active; - await Task.WhenAll(tasks.Select(async task => + await pauseIfNecessaryAsync(parameters, notification, notification.CancellationToken).ConfigureAwait(false); + + await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) => { - if (notification.CancellationToken.IsCancellationRequested) - return; + cancellation.ThrowIfCancellationRequested(); try { - var model = await Import(task, parameters, notification.CancellationToken).ConfigureAwait(false); + await pauseIfNecessaryAsync(parameters, notification, cancellation).ConfigureAwait(false); + + var model = await Import(task, parameters, cancellation).ConfigureAwait(false); lock (imported) { @@ -150,7 +144,7 @@ namespace osu.Game.Database { Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database); } - })).ConfigureAwait(false); + }).ConfigureAwait(false); if (imported.Count == 0) { @@ -297,8 +291,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) @@ -586,21 +578,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) From 72564b5c98c530f2ea39b98844e592528b44d439 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 12 Nov 2024 01:38:31 +0900 Subject: [PATCH 06/73] Make `CurrentPlaylistItem` not a bindable --- .../TestSceneDrawableLoungeRoom.cs | 20 ++-- .../Multiplayer/TestSceneDrawableRoom.cs | 94 +++++++++---------- .../Multiplayer/TestSceneMatchStartControl.cs | 22 ++--- .../TestSceneMultiplayerMatchFooter.cs | 9 +- .../TestSceneMultiplayerPlaylist.cs | 6 +- .../TestSceneMultiplayerSpectateButton.cs | 11 +-- .../UpdateableBeatmapBackgroundSprite.cs | 19 ++-- .../Online/Multiplayer/MultiplayerClient.cs | 4 +- osu.Game/Online/Rooms/Room.cs | 49 +++++++--- .../Components/OnlinePlayBackgroundSprite.cs | 41 -------- .../Lounge/Components/DrawableRoom.cs | 58 +++++++----- .../Lounge/Components/RoomsContainer.cs | 2 +- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 61 ++++++++---- .../OnlinePlay/Match/DrawableMatchRoom.cs | 34 +++---- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 3 +- .../Multiplayer/Match/MatchStartControl.cs | 14 ++- .../Match/MultiplayerMatchFooter.cs | 16 +++- .../Match/MultiplayerMatchSettingsOverlay.cs | 19 ++-- .../Match/MultiplayerSpectateButton.cs | 16 ++-- .../Match/Playlist/MultiplayerPlaylist.cs | 14 ++- .../Multiplayer/MultiplayerMatchSubScreen.cs | 36 ++++--- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 74 +++++---------- .../Multiplayer/TestMultiplayerClient.cs | 2 +- .../OnlinePlay/TestRoomRequestsHandler.cs | 9 +- 24 files changed, 326 insertions(+), 307 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs index 4de911b6b6..9fe7189a0a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.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 System.Threading; @@ -10,6 +8,7 @@ using System.Threading.Tasks; using Moq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; @@ -31,8 +30,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - private DrawableLoungeRoom drawableRoom; - private SearchTextBox searchTextBox; + private DrawableLoungeRoom drawableRoom = null!; + private SearchTextBox searchTextBox = null!; private readonly ManualResetEventSlim allowResponseCallback = new ManualResetEventSlim(); @@ -78,6 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Anchor = Anchor.Centre, Origin = Anchor.Centre, + SelectedRoom = new Bindable() } } }; @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFocusViaKeyboardCommit() { - DrawableLoungeRoom.PasswordEntryPopover popover = null; + DrawableLoungeRoom.PasswordEntryPopover? popover = null; AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); AddStep("click room twice", () => @@ -103,11 +103,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("enter password", () => popover.ChildrenOfType().Single().Text = "password"); AddStep("commit via enter", () => InputManager.Key(Key.Enter)); - AddAssert("popover has focus", () => checkFocus(popover)); + AddAssert("popover has focus", () => checkFocus(popover!)); AddStep("attempt another enter", () => InputManager.Key(Key.Enter)); - AddAssert("popover still has focus", () => checkFocus(popover)); + AddAssert("popover still has focus", () => checkFocus(popover!)); AddStep("unblock response", () => allowResponseCallback.Set()); @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestFocusViaMouseCommit() { - DrawableLoungeRoom.PasswordEntryPopover popover = null; + DrawableLoungeRoom.PasswordEntryPopover? popover = null; AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); AddStep("click room twice", () => @@ -144,11 +144,11 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddAssert("popover has focus", () => checkFocus(popover)); + AddAssert("popover has focus", () => checkFocus(popover!)); AddStep("attempt another click", () => InputManager.Click(MouseButton.Left)); - AddAssert("popover still has focus", () => checkFocus(popover)); + AddAssert("popover still has focus", () => checkFocus(popover!)); AddStep("unblock response", () => allowResponseCallback.Set()); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 98242e2d92..4c609613a3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -41,6 +41,31 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create rooms", () => { + PlaylistItem item1 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = { StarRating = 2.5 } + }.BeatmapInfo); + + PlaylistItem item2 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = { StarRating = 4.5 } + }.BeatmapInfo); + + PlaylistItem item3 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = + { + StarRating = 2.5, + Metadata = + { + Artist = "very very very very very very very very very long artist", + ArtistUnicode = "very very very very very very very very very long artist", + Title = "very very very very very very very very very very very long title", + TitleUnicode = "very very very very very very very very very very very long title", + } + } + }.BeatmapInfo); + Child = rooms = new FillFlowContainer { Anchor = Anchor.Centre, @@ -56,16 +81,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = { Value = MatchType.HeadToHead }, - Playlist = - { - new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 2.5 - } - }.BeatmapInfo) - } + Playlist = { item1 }, + CurrentPlaylistItem = item1 }), createLoungeRoom(new Room { @@ -74,46 +91,16 @@ namespace osu.Game.Tests.Visual.Multiplayer HasPassword = { Value = true }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = { Value = MatchType.HeadToHead }, - Playlist = - { - new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 2.5, - Metadata = - { - Artist = "very very very very very very very very very long artist", - ArtistUnicode = "very very very very very very very very very long artist", - Title = "very very very very very very very very very very very long title", - TitleUnicode = "very very very very very very very very very very very long title", - } - } - }.BeatmapInfo) - } + Playlist = { item3 }, + CurrentPlaylistItem = item3 }), createLoungeRoom(new Room { Name = { Value = "Playlist room with multiple beatmaps" }, Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - Playlist = - { - new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 2.5 - } - }.BeatmapInfo), - new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 4.5 - } - }.BeatmapInfo) - } + Playlist = { item1, item2 }, + CurrentPlaylistItem = item1 }), createLoungeRoom(new Room { @@ -181,20 +168,29 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = { Value = "A host-only room" }, QueueMode = { Value = QueueMode.HostOnly }, - Type = { Value = MatchType.HeadToHead } - }), + Type = { Value = MatchType.HeadToHead }, + }) + { + SelectedItem = new Bindable() + }, new DrawableMatchRoom(new Room { Name = { Value = "An all-players, team-versus room" }, QueueMode = { Value = QueueMode.AllPlayers }, Type = { Value = MatchType.TeamVersus } - }), + }) + { + SelectedItem = new Bindable() + }, new DrawableMatchRoom(new Room { Name = { Value = "A round-robin room" }, QueueMode = { Value = QueueMode.AllPlayersRoundRobin }, Type = { Value = MatchType.HeadToHead } - }), + }) + { + SelectedItem = new Bindable() + }, } }); } @@ -215,7 +211,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new DrawableLoungeRoom(room) { MatchingFilter = true, - SelectedRoom = { BindTarget = selectedRoom } + SelectedRoom = selectedRoom }; } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 3b10509895..4eddac8c7a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -43,9 +43,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private OsuButton readyButton => control.ChildrenOfType().Single(); - [Cached(typeof(IBindable))] - private readonly Bindable currentItem = new Bindable(); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; @@ -107,31 +104,33 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public void SetUpSteps() { + PlaylistItem item = null!; + AddStep("reset state", () => { multiplayerClient.Invocations.Clear(); beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable(); - currentItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo) + item = new PlaylistItem(Beatmap.Value.BeatmapInfo) { RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID }; room.Value = new Room { - Playlist = { currentItem.Value }, - CurrentPlaylistItem = { BindTarget = currentItem } + Playlist = { item }, + CurrentPlaylistItem = item }; - localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value }; + localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) + { + User = API.LocalUser.Value + }; multiplayerRoom = new MultiplayerRoom(0) { - Playlist = - { - TestMultiplayerClient.CreateMultiplayerPlaylistItem(currentItem.Value), - }, + Playlist = { TestMultiplayerClient.CreateMultiplayerPlaylistItem(item) }, Users = { localUser }, Host = localUser, }; @@ -144,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(250, 50), + SelectedItem = new Bindable(item) }; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs index 9d8ef76e75..edeb1708e0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,9 +12,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayerMatchFooter : MultiplayerTestScene { - [Cached(typeof(IBindable))] - private readonly Bindable currentItem = new Bindable(); - public override void SetUpSteps() { base.SetUpSteps(); @@ -33,7 +29,10 @@ namespace osu.Game.Tests.Visual.Multiplayer Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, Height = 50, - Child = new MultiplayerMatchFooter() + Child = new MultiplayerMatchFooter + { + SelectedItem = new Bindable() + } } }; }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index 3baabecd84..e12c2bee49 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!; @@ -56,7 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer 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() }; }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 5ae5d1e228..b4f55bb6a3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -26,9 +26,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayerSpectateButton : MultiplayerTestScene { - [Cached(typeof(IBindable))] - private readonly Bindable currentItem = new Bindable(); - private MultiplayerSpectateButton spectateButton = null!; private MatchStartControl startControl = null!; @@ -51,13 +48,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create button", () => { - AvailabilityTracker.SelectedItem.BindTo(currentItem); + PlaylistItem item = SelectedRoom.Value.Playlist.First(); + + AvailabilityTracker.SelectedItem.Value = item; importedSet = beatmaps.GetAllUsableBeatmapSets().First(); Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); - currentItem.Value = SelectedRoom.Value.Playlist.First(); - Child = new PopoverContainer { RelativeSizeAxes = Axes.Both, @@ -72,12 +69,14 @@ namespace osu.Game.Tests.Visual.Multiplayer Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200, 50), + SelectedItem = new Bindable(item) }, startControl = new MatchStartControl { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(200, 50), + SelectedItem = new Bindable(item) } } } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 6f71fa90b8..8ddda485b5 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 = 500; + + /// + /// Delay before the background is unloaded while off-screen. + /// + public double BackgroundUnloadDelay = 10000; [Resolved] private BeatmapManager beatmaps { get; set; } = null!; @@ -29,10 +37,9 @@ namespace osu.Game.Beatmaps.Drawables this.beatmapSetCoverType = beatmapSetCoverType; } - /// - /// Delay before the background is unloaded while off-screen. - /// - protected virtual double UnloadDelay => 10000; + protected override double LoadDelay => BackgroundLoadDelay; + + protected virtual double UnloadDelay => BackgroundUnloadDelay; protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index ff147aba10..5519d58060 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -203,7 +203,7 @@ namespace osu.Game.Online.Multiplayer 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.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; @@ -847,7 +847,7 @@ namespace osu.Game.Online.Multiplayer 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.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId); APIRoom.AutoSkip.Value = Room.Settings.AutoSkip; RoomUpdated?.Invoke(); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index c39932c3bf..1f5fec6089 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -1,10 +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.Linq; +using System.Runtime.CompilerServices; using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -16,8 +17,25 @@ using osu.Game.Online.Rooms.RoomStatuses; namespace osu.Game.Online.Rooms { [JsonObject(MemberSerialization.OptIn)] - public partial class Room : IDependencyInjectionCandidate + public partial class Room : IDependencyInjectionCandidate, INotifyPropertyChanged { + public event PropertyChangedEventHandler? PropertyChanged; + + [JsonProperty("current_playlist_item")] + private PlaylistItem? currentPlaylistItem; + + /// + /// 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); + } + [Cached] [JsonProperty("id")] public readonly Bindable RoomID = new Bindable(); @@ -28,7 +46,7 @@ namespace osu.Game.Online.Rooms [Cached] [JsonProperty("host")] - public readonly Bindable Host = new Bindable(); + public readonly Bindable Host = new Bindable(); [Cached] [JsonProperty("playlist")] @@ -38,10 +56,6 @@ namespace osu.Game.Online.Rooms [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(); @@ -126,7 +140,7 @@ namespace osu.Game.Online.Rooms [Cached(Name = nameof(Password))] [JsonProperty("password")] - public readonly Bindable Password = new Bindable(); + public readonly Bindable Password = new Bindable(); [Cached] public readonly Bindable Duration = new Bindable(); @@ -203,7 +217,7 @@ namespace osu.Game.Online.Rooms AutoStartDuration.Value = other.AutoStartDuration.Value; DifficultyRange.Value = other.DifficultyRange.Value; PlaylistItemStats.Value = other.PlaylistItemStats.Value; - CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value; + CurrentPlaylistItem = other.CurrentPlaylistItem; AutoSkip.Value = other.AutoSkip.Value; other.RemoveExpiredPlaylistItems(); @@ -240,7 +254,7 @@ namespace osu.Game.Online.Rooms public int CountTotal; [JsonProperty("ruleset_ids")] - public int[] RulesetIDs; + public int[] RulesetIDs = []; } [JsonObject(MemberSerialization.OptIn)] @@ -252,5 +266,18 @@ 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 SetField(ref T field, T value, [CallerMemberName] string propertyName = null!) + { + if (EqualityComparer.Default.Equals(field, value)) + return false; + + field = value; + OnPropertyChanged(propertyName); + return true; + } } } diff --git a/osu.Game/Screens/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/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index ef06d21655..59c8047062 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.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.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -16,6 +15,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; @@ -29,27 +29,28 @@ using osuTK.Graphics; 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 UpdateableBeatmapBackgroundSprite background = null!; + private DelayedLoadWrapper wrapper = null!; - private DelayedLoadWrapper wrapper; - - public DrawableRoom(Room room) + protected DrawableRoom(Room room) { Room = room; @@ -77,7 +78,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 +86,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; }), @@ -186,7 +187,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Font = OsuFont.GetFont(size: 28), Current = { BindTarget = Room.Name } }, - new RoomStatusText() + new RoomStatusText + { + SelectedItem = { BindTarget = SelectedItem } + } } } }, @@ -245,6 +249,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components wrapper.DelayedLoadComplete += _ => { + Debug.Assert(specialCategoryPill != null); + Debug.Assert(endDateInfo != null); + Debug.Assert(passwordIcon != null); + wrapper.FadeInFromZero(200); roomCategory.BindTo(Room.Category); @@ -265,6 +273,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hasPassword.BindTo(Room.HasPassword); hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); }; + + SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -289,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite(); + protected virtual UpdateableBeatmapBackgroundSprite CreateBackground() => new UpdateableBeatmapBackgroundSprite(); protected virtual IEnumerable CreateBottomDetails() { @@ -324,14 +334,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private partial class RoomStatusText : OnlinePlayComposite { - [Resolved] - private OsuColour colours { get; set; } + 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!; + + private SpriteText statusText = null!; + private LinkFlowContainer beatmapText = null!; public RoomStatusText() { @@ -383,12 +395,12 @@ 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(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index e842f8c436..9e60f43ed7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -140,7 +140,7 @@ 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); } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index fed47e847a..36db1ab29b 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) @@ -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); @@ -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,14 +174,14 @@ 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; } @@ -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/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index 3bda93c909..5d4b6b6404 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -1,10 +1,7 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -21,25 +18,22 @@ namespace osu.Game.Screens.OnlinePlay.Match { public partial class DrawableMatchRoom : DrawableRoom { - public readonly IBindable SelectedItem = new Bindable(); - public Action OnEdit; + public Action? OnEdit; [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; - private readonly IBindable host = new Bindable(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly IBindable host = new Bindable(); 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; + base.SelectedItem.BindTo(SelectedItem); host.BindTo(room.Host); } @@ -58,21 +52,23 @@ namespace osu.Game.Screens.OnlinePlay.Match } } + public new required Bindable SelectedItem + { + get => current; + set => current.Current = value; + } + protected override void LoadComplete() { 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); } - protected override Drawable CreateBackground() => background = new BackgroundSprite(); - - private partial class BackgroundSprite : UpdateableBeatmapBackgroundSprite + protected override UpdateableBeatmapBackgroundSprite CreateBackground() => base.CreateBackground().With(d => { - protected override double LoadDelay => 0; - } + d.BackgroundLoadDelay = 0; + }); } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 7c8931c04e..9801405dd3 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -35,7 +35,6 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IPreviewTrackOwner))] public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner { - [Cached(typeof(IBindable))] public readonly Bindable SelectedItem = new Bindable(); public override bool? ApplyModTrackAdjustments => true; @@ -164,7 +163,7 @@ namespace osu.Game.Screens.OnlinePlay.Match new DrawableMatchRoom(Room, allowEdit) { OnEdit = () => settingsOverlay.Show(), - SelectedItem = { BindTarget = SelectedItem } + SelectedItem = SelectedItem } }, null, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index a82fa6e4bc..0d90d44496 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -23,6 +23,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public partial class MatchStartControl : CompositeDrawable { + public required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } + [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!; @@ -32,9 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private MultiplayerClient client { get; set; } = null!; - [Resolved] - private IBindable currentItem { get; set; } = null!; - + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); private readonly MultiplayerReadyButton readyButton; private readonly MultiplayerCountdownButton countdownButton; @@ -94,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.LoadComplete(); - currentItem.BindValueChanged(_ => updateState()); + SelectedItem.BindValueChanged(_ => updateState()); client.RoomUpdated += onRoomUpdated; client.LoadRequested += onLoadRequested; updateState(); @@ -210,7 +214,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match readyButton.Enabled.Value = countdownButton.Enabled.Value = client.Room.State != MultiplayerRoomState.Closed - && currentItem.Value?.ID == client.Room.Settings.PlaylistItemId + && SelectedItem.Value?.ID == client.Room.Settings.PlaylistItemId && !client.Room.Playlist.Single(i => i.ID == client.Room.Settings.PlaylistItemId).Expired && !operationInProgress.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index fcb6480b58..2b592bd8b9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -1,10 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { @@ -13,6 +13,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private const float ready_button_width = 600; private const float spectate_button_width = 200; + public required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } + + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); + public MultiplayerMatchFooter() { RelativeSizeAxes = Axes.Both; @@ -22,17 +30,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.Both, Content = new[] { - new Drawable[] + new Drawable?[] { null, new MultiplayerSpectateButton { RelativeSizeAxes = Axes.Both, + SelectedItem = selectedItem }, null, new MatchStartControl { RelativeSizeAxes = Axes.Both, + SelectedItem = selectedItem }, null } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 5446211ced..7a1868bfbb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -28,14 +28,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public partial class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay { - private MatchSettings settings = null!; + public required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } protected override OsuButton SubmitButton => settings.ApplyButton; + protected override bool IsLoading => ongoingOperationTracker.InProgress.Value; [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!; - protected override bool IsLoading => ongoingOperationTracker.InProgress.Value; + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); + private MatchSettings settings = null!; public MultiplayerMatchSettingsOverlay(Room room) : base(room) @@ -48,7 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, - SettingsApplied = Hide + SettingsApplied = Hide, + SelectedItem = { BindTarget = SelectedItem } }; protected partial class MatchSettings : OnlinePlayComposite @@ -57,6 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public readonly Bindable SelectedItem = new Bindable(); public Action? SettingsApplied; public OsuTextBox NameField = null!; @@ -66,7 +74,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public OsuTextBox PasswordTextBox = null!; public OsuCheckbox AutoSkipCheckbox = null!; public RoundedButton ApplyButton = null!; - public OsuSpriteText ErrorText = null!; private OsuEnumDropdown startModeDropdown = null!; @@ -367,7 +374,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match base.LoadComplete(); drawablePlaylist.Items.BindTo(Playlist); - drawablePlaylist.SelectedItem.BindTo(CurrentPlaylistItem); + drawablePlaylist.SelectedItem.BindTo(SelectedItem); } protected override void Update() @@ -448,7 +455,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (text.StartsWith(not_found_prefix, StringComparison.Ordinal)) { ErrorText.Text = "The selected beatmap is not available online."; - CurrentPlaylistItem.Value.MarkInvalid(); + SelectedItem.Value?.MarkInvalid(); } else { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs index 92edc9b979..905a2bf31b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs @@ -21,6 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public partial class MultiplayerSpectateButton : CompositeDrawable { + public required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } + [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!; @@ -30,13 +36,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private MultiplayerClient client { get; set; } = null!; - [Resolved] - private IBindable currentItem { get; set; } = null!; + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); + private readonly RoundedButton button; private IBindable operationInProgress = null!; - private readonly RoundedButton button; - public MultiplayerSpectateButton() { InternalChild = button = new RoundedButton @@ -71,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.LoadComplete(); - currentItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true); + SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true); client.RoomUpdated += onRoomUpdated; updateState(); } @@ -117,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void checkForAutomaticDownload() { - PlaylistItem? item = currentItem.Value; + PlaylistItem? item = SelectedItem.Value; downloadCheckCancellation?.Cancel(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs index 8ba85019d5..a44891e934 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs @@ -19,6 +19,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { public readonly Bindable DisplayMode = new Bindable(); + public required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } + /// /// Invoked when an item requests to be edited. /// @@ -27,9 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist [Resolved] private MultiplayerClient client { get; set; } = null!; - [Resolved] - private IBindable currentItem { get; set; } = null!; - + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); private MultiplayerPlaylistTabControl playlistTabControl = null!; private MultiplayerQueueList queueList = null!; private MultiplayerHistoryList historyList = null!; @@ -58,14 +62,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist queueList = new MultiplayerQueueList { RelativeSizeAxes = Axes.Both, - SelectedItem = { BindTarget = currentItem }, + SelectedItem = { BindTarget = selectedItem }, RequestEdit = item => RequestEdit?.Invoke(item) }, historyList = new MultiplayerHistoryList { RelativeSizeAxes = Axes.Both, Alpha = 0, - SelectedItem = { BindTarget = currentItem } + SelectedItem = { BindTarget = selectedItem } } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index a37314de0e..e0c8675ec5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -1,13 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; @@ -45,12 +44,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override string ShortTitle => "room"; [Resolved] - private MultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } = null!; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private OsuGame? game { get; set; } - private AddItemButton addItemButton; + private AddItemButton addItemButton = null!; public MultiplayerMatchSubScreen(Room room) : base(room) @@ -95,7 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, Content = new[] { - new Drawable[] + new Drawable?[] { // Participants column new GridContainer @@ -142,7 +141,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerPlaylist { RelativeSizeAxes = Axes.Both, - RequestEdit = OpenSongSelection + RequestEdit = OpenSongSelection, + SelectedItem = SelectedItem } }, new[] @@ -220,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer /// Opens the song selection screen to add or edit an item. /// /// An optional playlist item to edit. If null, a new item will be added instead. - internal void OpenSongSelection(PlaylistItem itemToEdit = null) + internal void OpenSongSelection(PlaylistItem? itemToEdit = null) { if (!this.IsCurrentScreen()) return; @@ -228,9 +228,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit)); } - protected override Drawable CreateFooter() => new MultiplayerMatchFooter(); + protected override Drawable CreateFooter() => new MultiplayerMatchFooter + { + SelectedItem = SelectedItem + }; - protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room); + protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room) + { + SelectedItem = SelectedItem + }; protected override void UpdateMods() { @@ -245,7 +251,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } [Resolved(canBeNull: true)] - private IDialogOverlay dialogOverlay { get; set; } + private IDialogOverlay? dialogOverlay { get; set; } private bool exitConfirmed; @@ -275,8 +281,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return base.OnExiting(e); } - private ModSettingChangeTracker modSettingChangeTracker; - private ScheduledDelegate debouncedModSettingsUpdate; + private ModSettingChangeTracker? modSettingChangeTracker; + private ScheduledDelegate? debouncedModSettingsUpdate; private void onUserModsChanged(ValueChangedEvent> mods) { @@ -422,7 +428,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return; // If there's only one playlist item and we are the host, assume we want to change it. Else add a new one. - PlaylistItem itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null; + PlaylistItem? itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null; OpenSongSelection(itemToEdit); @@ -434,7 +440,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.Dispose(isDisposing); - if (client != null) + if (client.IsNotNull()) { client.RoomUpdated -= onRoomUpdated; client.LoadRequested -= onLoadRequested; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 5be5c4b4f4..0d96f348ac 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.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 osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Screens.OnlinePlay.Match; namespace osu.Game.Screens.OnlinePlay { @@ -20,96 +17,69 @@ namespace osu.Game.Screens.OnlinePlay public partial class OnlinePlayComposite : CompositeDrawable { [Resolved(typeof(Room))] - protected Bindable RoomID { get; private set; } + protected Bindable RoomID { get; private set; } = null!; [Resolved(typeof(Room), nameof(Room.Name))] - protected Bindable RoomName { get; private set; } + protected Bindable RoomName { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Host { get; private set; } + protected Bindable Host { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Status { get; private set; } + protected Bindable Status { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Type { get; private set; } - - /// - /// The currently selected item in the , or the current item from - /// if this is not within a . - /// - [Resolved(typeof(Room))] - protected Bindable CurrentPlaylistItem { get; private set; } + protected Bindable Type { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable PlaylistItemStats { get; private set; } + protected Bindable PlaylistItemStats { get; private set; } = null!; [Resolved(typeof(Room))] - protected BindableList Playlist { get; private set; } + protected BindableList Playlist { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable DifficultyRange { get; private set; } + protected Bindable DifficultyRange { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Category { get; private set; } + protected Bindable Category { get; private set; } = null!; [Resolved(typeof(Room))] - protected BindableList RecentParticipants { get; private set; } + protected BindableList RecentParticipants { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable ParticipantCount { get; private set; } + protected Bindable ParticipantCount { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable MaxParticipants { get; private set; } + protected Bindable MaxParticipants { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable MaxAttempts { get; private set; } + protected Bindable MaxAttempts { get; private set; } = null!; [Resolved(typeof(Room))] - public Bindable UserScore { get; private set; } + public Bindable UserScore { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable StartDate { get; private set; } + protected Bindable StartDate { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable EndDate { get; private set; } + protected Bindable EndDate { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Availability { get; private set; } + protected Bindable Availability { get; private set; } = null!; [Resolved(typeof(Room))] - public Bindable Password { get; private set; } + public Bindable Password { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable Duration { get; private set; } + protected Bindable Duration { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable QueueMode { get; private set; } + protected Bindable QueueMode { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable AutoStartDuration { get; private set; } + protected Bindable AutoStartDuration { get; private set; } = null!; [Resolved(typeof(Room))] - protected Bindable AutoSkip { get; private set; } - - [Resolved(CanBeNull = true)] - private IBindable subScreenSelectedItem { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - subScreenSelectedItem?.BindValueChanged(_ => UpdateSelectedItem()); - Playlist.BindCollectionChanged((_, _) => UpdateSelectedItem(), true); - } - - protected void UpdateSelectedItem() - { - // null room ID means this is a room in the process of being created. - if (RoomID.Value == null) - CurrentPlaylistItem.Value = Playlist.GetCurrentItem(); - else if (subScreenSelectedItem != null) - CurrentPlaylistItem.Value = subScreenSelectedItem.Value; - } + protected Bindable AutoSkip { get; private set; } = null!; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index efa9dc4990..65a4019bb5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = ServerAPIRoom.Name.Value, MatchType = ServerAPIRoom.Type.Value, - Password = password, + Password = password ?? string.Empty, QueueMode = ServerAPIRoom.QueueMode.Value, AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value }, diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index ec89f81597..41d288ba60 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.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.Diagnostics; @@ -296,10 +294,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay Debug.Assert(result != null); // Playlist item IDs and beatmaps aren't serialised. - if (source.CurrentPlaylistItem.Value != null) + if (source.CurrentPlaylistItem != null) { - result.CurrentPlaylistItem.Value = result.CurrentPlaylistItem.Value.With(new Optional(source.CurrentPlaylistItem.Value.Beatmap)); - result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID; + Debug.Assert(result.CurrentPlaylistItem != null); + result.CurrentPlaylistItem = result.CurrentPlaylistItem.With(new Optional(source.CurrentPlaylistItem.Beatmap)); + result.CurrentPlaylistItem.ID = source.CurrentPlaylistItem.ID; } for (int i = 0; i < source.Playlist.Count; i++) From 8d5cd2b3531c86f8b0e85c769c2a610e1dd6b881 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 12 Nov 2024 20:12:50 +0900 Subject: [PATCH 07/73] Fix inspection --- .../Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 0883c626fe..d518565be9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select first room", () => container.Rooms.First().TriggerClick()); AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); - AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID?.Value))); + AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID.Value))); AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category.Value == RoomCategory.Spotlight))); From f3fd87af7bb083dc97d054d3a0c6b731acdeca59 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 12 Nov 2024 20:32:59 +0900 Subject: [PATCH 08/73] Make `DrawableMatchRoom` structurally match other implementations --- .../Screens/OnlinePlay/Match/DrawableMatchRoom.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index 5d4b6b6404..e5d3bd1586 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -20,10 +20,16 @@ namespace osu.Game.Screens.OnlinePlay.Match { public Action? OnEdit; + public new required Bindable SelectedItem + { + get => selectedItem; + set => selectedItem.Current = value; + } + [Resolved] private IAPIProvider api { get; set; } = null!; - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); private readonly IBindable host = new Bindable(); private readonly bool allowEdit; private Drawable? editButton; @@ -52,12 +58,6 @@ namespace osu.Game.Screens.OnlinePlay.Match } } - public new required Bindable SelectedItem - { - get => current; - set => current.Current = value; - } - protected override void LoadComplete() { base.LoadComplete(); From 48212dfaebe9a5e941f3a1eac413b6ccfe488cf6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Nov 2024 21:08:06 +0900 Subject: [PATCH 09/73] Fix test failures due to early disposal of import stream --- .../Online/TestSceneReplayMissingBeatmap.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) 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) From db025d81eea10954d12ab31f11f2ea61116c853e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 15:50:01 +0900 Subject: [PATCH 10/73] Reorder public property vs private field --- osu.Game/Online/Rooms/Room.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 1f5fec6089..6a4d14189d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -21,9 +21,6 @@ namespace osu.Game.Online.Rooms { public event PropertyChangedEventHandler? PropertyChanged; - [JsonProperty("current_playlist_item")] - private PlaylistItem? currentPlaylistItem; - /// /// Represents the current item selected within the room. /// @@ -36,6 +33,9 @@ namespace osu.Game.Online.Rooms set => SetField(ref currentPlaylistItem, value); } + [JsonProperty("current_playlist_item")] + private PlaylistItem? currentPlaylistItem; + [Cached] [JsonProperty("id")] public readonly Bindable RoomID = new Bindable(); From 99762da7b8fd5f1439d4a3e2ff5f8dd6b88964b9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 16:28:39 +0900 Subject: [PATCH 11/73] Make RoomID non-bindable Most important changes are to `RoomSubScreen` and `PlaylistsRoomSubScreen`, because those are the only two cases that now bind to the event instead. --- .../StatefulMultiplayerClientTest.cs | 2 +- .../DailyChallenge/TestSceneDailyChallenge.cs | 6 +- .../TestSceneDailyChallengeIntro.cs | 2 +- .../TestSceneDailyChallengeLeaderboard.cs | 4 +- .../Visual/Menus/TestSceneMainMenu.cs | 2 +- .../Multiplayer/TestSceneMatchLeaderboard.cs | 2 +- .../TestScenePlaylistsParticipantsList.cs | 2 +- .../UserInterface/TestSceneMainMenuButton.cs | 4 +- .../Online/Multiplayer/MultiplayerClient.cs | 4 +- osu.Game/Online/Rooms/JoinRoomRequest.cs | 2 +- osu.Game/Online/Rooms/PartRoomRequest.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 18 ++++-- osu.Game/Screens/Menu/DailyChallengeButton.cs | 4 +- .../OnlinePlay/Components/RoomManager.cs | 28 ++++----- .../Components/SelectionPollingComponent.cs | 8 +-- .../DailyChallenge/DailyChallenge.cs | 10 ++-- .../DailyChallengeLeaderboard.cs | 2 +- .../Lounge/Components/RoomsContainer.cs | 23 ++++--- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 8 +-- .../Match/Components/MatchChatDisplay.cs | 2 +- .../Match/Components/RoomSettingsOverlay.cs | 4 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 58 ++++++++++-------- .../Match/MultiplayerMatchSettingsOverlay.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Multiplayer/MultiplayerPlayer.cs | 23 ++++--- .../Multiplayer/MultiplayerRoomManager.cs | 12 ++-- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 - .../OnlinePlay/Playlists/PlaylistsPlayer.cs | 10 ++-- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- .../Playlists/PlaylistsRoomSubScreen.cs | 60 ++++++++++++------- osu.Game/Screens/Play/RoomSubmittingPlayer.cs | 12 ++-- .../Multiplayer/TestMultiplayerClient.cs | 2 +- .../Visual/OnlinePlay/TestRoomManager.cs | 10 ++-- .../OnlinePlay/TestRoomRequestsHandler.cs | 6 +- osu.Game/Users/UserActivity.cs | 2 +- 35 files changed, 176 insertions(+), 167 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index b4bbe274a5..2e9170019c 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer var newRoom = new Room(); newRoom.CopyFrom(SelectedRoom.Value); - newRoom.RoomID.Value = null; + newRoom.RoomID = null; MultiplayerClient.RoomSetupAction = room => { room.State = MultiplayerRoomState.Playing; diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index 2791563954..488289cb74 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge { var room = new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Name = { Value = "Daily Challenge: June 4, 2024" }, Playlist = { @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge { var room = new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Name = { Value = "Daily Challenge: June 4, 2024" }, Playlist = { @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge { var room = new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Name = { Value = "Daily Challenge: June 4, 2024" }, Playlist = { diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs index f1a2d6b5f2..c08398ac17 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge { API.Perform(new CreateRoomRequest(room = new Room { - RoomID = { Value = roomId }, + RoomID = roomId, Name = { Value = "Daily Challenge: June 4, 2024" }, Playlist = { diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs index 5fff6bb010..d21ca22e1b 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeLeaderboard.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge return false; }; }); - AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) + AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = 1 }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge return false; }; }); - AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) + AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = 1 }, new PlaylistItem(Beatmap.Value.BeatmapInfo)) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index aab3716463..8b81516a34 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Menus beatmap.OnlineID = 1001; getRoomRequest.TriggerSuccess(new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Name = { Value = "Aug 8, 2024" }, Playlist = { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index ea8fe8873d..f96e0bdd4c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create leaderboard", () => { - SelectedRoom.Value = new Room { RoomID = { Value = 3 } }; + SelectedRoom.Value = new Room { RoomID = 3 }; Child = new MatchLeaderboard { diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index 3b60c28dc0..b9e5875e06 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("create list", () => { - SelectedRoom.Value = new Room { RoomID = { Value = 7 } }; + SelectedRoom.Value = new Room { RoomID = 7 }; for (int i = 0; i < 50; i++) { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs index 41543669eb..3894b6ea5b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface beatmap.OnlineID = 1001; getRoomRequest.TriggerSuccess(new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Playlist = { new PlaylistItem(beatmap) @@ -131,7 +131,7 @@ namespace osu.Game.Tests.Visual.UserInterface beatmap.OnlineID = 1001; getRoomRequest.TriggerSuccess(new Room { - RoomID = { Value = 1234 }, + RoomID = 1234, Playlist = { new PlaylistItem(beatmap) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 5519d58060..bd0fa0cb72 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -181,10 +181,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.Value).ConfigureAwait(false); Debug.Assert(joinedRoom != null); // Populate users. 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/Room.cs b/osu.Game/Online/Rooms/Room.cs index 6a4d14189d..de854596e9 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -21,6 +21,15 @@ namespace osu.Game.Online.Rooms { public event PropertyChangedEventHandler? PropertyChanged; + /// + /// The online room ID. Will be null while the room has not yet been created. + /// + public long? RoomID + { + get => roomId; + set => SetField(ref roomId, value); + } + /// /// Represents the current item selected within the room. /// @@ -33,13 +42,12 @@ namespace osu.Game.Online.Rooms set => SetField(ref currentPlaylistItem, value); } + [JsonProperty("id")] + private long? roomId; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; - [Cached] - [JsonProperty("id")] - public readonly Bindable RoomID = new Bindable(); - [Cached] [JsonProperty("name")] public readonly Bindable Name = new Bindable(); @@ -196,7 +204,7 @@ namespace osu.Game.Online.Rooms /// The to copy values from. public void CopyFrom(Room other) { - RoomID.Value = other.RoomID.Value; + RoomID = other.RoomID; Name.Value = other.Name.Value; Category.Value = other.Category.Value; diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 4dbebf0ae9..0013c9e033 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -155,9 +155,9 @@ 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.Value != null && room.RoomID != lastDailyChallengeRoomID) { - lastDailyChallengeRoomID = room.RoomID.Value; + lastDailyChallengeRoomID = room.RoomID; // new challenge is live, reset intro played static. statics.SetValue(Static.DailyChallengeIntroPlayed, false); diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index cb27d1ee61..555a1f66bd 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,7 +40,7 @@ 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; @@ -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 @@ -128,7 +124,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { Logger.Error(ex, $"Failed to update room: {room.Name.Value}."); - 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/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 1aaf0a4321..968345c713 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -353,12 +353,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 +410,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 +437,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 +489,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/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/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 9e60f43ed7..3d9b7a46d0 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 => { @@ -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) { @@ -142,7 +141,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components foreach (var room in rooms) roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = SelectedRoom }); - applyFilterCriteria(Filter?.Value); + applyFilterCriteria(Filter.Value); } private void removeRooms(IEnumerable rooms) @@ -173,7 +172,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.SetLayoutPosition(room, room.Room.Category.Value > 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/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 3792a67896..a2eba3188d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -259,7 +259,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge selectionLease.Return(); selectionLease = null; - if (SelectedRoom.Value?.RoomID.Value == null) + if (SelectedRoom.Value?.RoomID == null) SelectedRoom.Value = new Room(); music?.EnsurePlayingSomething(); @@ -326,19 +326,19 @@ 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; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index 8dc1704fcd..ed4f0ad6d8 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components private void updateChannel() { - if (room.RoomID.Value == null || channelId.Value == 0) + if (room.RoomID == null || channelId.Value == 0) return; Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" }); diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 916b799d50..14d506a368 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 OnlinePlayComposite Settings { get; set; } = null!; protected override bool BlockScrollInput => false; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 9801405dd3..1b7df9c213 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using JetBrains.Annotations; @@ -29,6 +30,7 @@ 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 { @@ -59,8 +61,6 @@ namespace osu.Game.Screens.OnlinePlay.Match /// protected readonly Bindable> UserMods = new Bindable>(Array.Empty()); - protected readonly IBindable RoomId = new Bindable(); - [Resolved(CanBeNull = true)] private IOverlayManager overlayManager { get; set; } @@ -82,6 +82,9 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved] private PreviewTrackManager previewTrackManager { get; set; } = null!; + [Resolved(canBeNull: true)] + private IDialogOverlay dialogOverlay { get; set; } + [Cached] private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); @@ -109,8 +112,6 @@ namespace osu.Game.Screens.OnlinePlay.Match this.allowEdit = allowEdit; Padding = new MarginPadding { Top = Header.HEIGHT }; - - RoomId.BindTo(room.RoomID); } [BackgroundDependencyLoader] @@ -252,22 +253,6 @@ namespace osu.Game.Screens.OnlinePlay.Match { base.LoadComplete(); - RoomId.BindValueChanged(id => - { - if (id.NewValue == null) - { - // A new room is being created. - // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. - mainContent.Hide(); - settingsOverlay.Show(); - } - else - { - mainContent.Show(); - settingsOverlay.Hide(); - } - }, true); - SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods)); @@ -275,6 +260,31 @@ namespace osu.Game.Screens.OnlinePlay.Match beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap()); userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay); + + Room.PropertyChanged += onRoomPropertyChanged; + updateSetupState(); + } + + private void onRoomPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.RoomID)) + updateSetupState(); + } + + private void updateSetupState() + { + if (Room.RoomID == null) + { + // A new room is being created. + // The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed. + mainContent.Hide(); + settingsOverlay.Show(); + } + else + { + mainContent.Show(); + settingsOverlay.Hide(); + } } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -285,14 +295,11 @@ namespace osu.Game.Screens.OnlinePlay.Match }; } - [Resolved(canBeNull: true)] - private IDialogOverlay dialogOverlay { get; set; } - protected virtual bool IsConnected => api.State.Value == APIState.Online; public override bool OnBackButton() { - if (Room.RoomID.Value == null) + if (Room.RoomID == null) { if (!ensureExitConfirmed()) return true; @@ -365,7 +372,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (!IsConnected) return true; - bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0; + bool hasUnsavedChanges = Room.RoomID == null && Room.Playlist.Count > 0; if (dialogOverlay == null || !hasUnsavedChanges) return true; @@ -538,6 +545,7 @@ namespace osu.Game.Screens.OnlinePlay.Match base.Dispose(isDisposing); userModsSelectOverlayRegistration?.Dispose(); + Room.PropertyChanged -= onRoomPropertyChanged; } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 7a1868bfbb..edcad9c3ac 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -353,7 +353,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); - RoomID.BindValueChanged(roomId => playlistContainer.Alpha = roomId.NewValue == null ? 1 : 0, true); Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); @@ -382,6 +381,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match base.Update(); ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value; + playlistContainer.Alpha = room.RoomID == null ? 1 : 0; } private void apply() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index e0c8675ec5..696eb58c3f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSubScreen(Room room) : base(room) { - Title = room.RoomID.Value == null ? "New room" : room.Name.Value; + Title = room.RoomID == null ? "New room" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index e560c5ca5d..d444af2fd3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -1,14 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; @@ -29,17 +28,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); [Resolved] - private MultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } = null!; - private IBindable isConnected; + private IBindable isConnected = null!; private readonly TaskCompletionSource resultsReady = new TaskCompletionSource(); - private readonly MultiplayerRoomUser[] users; - private LoadingLayer loadingDisplay; - - private MultiplayerGameplayLeaderboard multiplayerLeaderboard; + private LoadingLayer loadingDisplay = null!; + private MultiplayerGameplayLeaderboard multiplayerLeaderboard = null!; /// /// Construct a multiplayer player. @@ -153,7 +150,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer GameplayClockContainer.Reset(); } - private void failAndBail(string message = null) + private void failAndBail(string? message = null) { if (!string.IsNullOrEmpty(message)) Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); @@ -196,14 +193,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override ResultsScreen CreateResults(ScoreInfo score) { - Debug.Assert(Room.RoomID.Value != null); + Debug.Assert(Room.RoomID != null); return multiplayerLeaderboard.TeamScores.Count == 2 - ? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem, multiplayerLeaderboard.TeamScores) + ? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value, PlaylistItem, multiplayerLeaderboard.TeamScores) { ShowUserStatistics = true, } - : new MultiplayerResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem) + : new MultiplayerResultsScreen(score, Room.RoomID.Value, PlaylistItem) { ShowUserStatistics = true }; @@ -213,7 +210,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.Dispose(isDisposing); - if (client != null) + if (client.IsNotNull()) { client.GameplayStarted -= onGameplayStarted; client.ResultsReady -= onResultsReady; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 5f51ccc8d4..d7efd30435 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Diagnostics; using osu.Framework.Allocation; @@ -18,12 +16,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public partial class MultiplayerRoomManager : RoomManager { [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; - public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) + public override void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null) => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); - public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) + public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null) { if (!multiplayerClient.IsConnected.Value) { @@ -51,9 +49,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer multiplayerClient.LeaveRoom(); } - private void joinMultiplayerRoom(Room room, string password, Action onSuccess = null, Action onError = null) + private void joinMultiplayerRoom(Room room, string? password, Action? onSuccess = null, Action? onError = null) { - Debug.Assert(room.RoomID.Value != null); + Debug.Assert(room.RoomID != null); multiplayerClient.JoinRoom(room, password).ContinueWith(t => { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 0d96f348ac..9911a08b81 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -16,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay /// public partial class OnlinePlayComposite : CompositeDrawable { - [Resolved(typeof(Room))] - protected Bindable RoomID { get; private set; } = null!; - [Resolved(typeof(Room), nameof(Room.Name))] protected Bindable RoomName { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 4a2d8f8f6b..7ca09b5563 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Diagnostics; using System.Linq; @@ -21,11 +19,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsPlayer : RoomSubmittingPlayer { - public Action Exited; + public Action? Exited; protected override UserActivity InitialActivity => new UserActivity.InPlaylistGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); - public PlaylistsPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration configuration = null) + public PlaylistsPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration? configuration = null) : base(room, playlistItem, configuration) { } @@ -57,8 +55,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override ResultsScreen CreateResults(ScoreInfo score) { - Debug.Assert(Room.RoomID.Value != null); - return new PlaylistItemUserResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem) + Debug.Assert(Room.RoomID != null); + return new PlaylistItemUserResultsScreen(score, Room.RoomID.Value, PlaylistItem) { AllowRetry = true, ShowUserStatistics = true, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 9166cac9de..4d85206473 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -372,7 +372,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void onPlaylistChanged(object? sender, NotifyCollectionChangedEventArgs e) => playlistLength.Text = $"Length: {Playlist.GetTotalDuration(rulesets)}"; - private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0 + private bool hasValidSettings => room.RoomID == null && NameField.Text.Length > 0 && Playlist.Count > 0 && hasValidDuration; private bool hasValidDuration => DurationField.Current.Value <= TimeSpan.FromDays(14) || localUser.Value.IsSupporter; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 3126bbf2eb..9667e210a4 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -1,11 +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.ComponentModel; using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -22,6 +20,7 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Screens.OnlinePlay.Playlists { @@ -33,20 +32,22 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private readonly IBindable isIdle = new BindableBool(); - private MatchLeaderboard leaderboard; - private SelectionPollingComponent selectionPollingComponent; + [Resolved(CanBeNull = true)] + private IdleTracker? idleTracker { get; set; } - private FillFlowContainer progressSection; + private MatchLeaderboard leaderboard = null!; + private SelectionPollingComponent selectionPollingComponent = null!; + private FillFlowContainer progressSection = null!; public PlaylistsRoomSubScreen(Room room) : base(room, false) // Editing is temporarily not allowed. { - Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; + Title = room.RoomID == null ? "New playlist" : room.Name.Value; Activity.Value = new UserActivity.InLobby(room); } - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] IdleTracker idleTracker) + [BackgroundDependencyLoader] + private void load() { if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); @@ -59,17 +60,26 @@ namespace osu.Game.Screens.OnlinePlay.Playlists base.LoadComplete(); isIdle.BindValueChanged(_ => updatePollingRate(), true); - RoomId.BindValueChanged(id => - { - if (id.NewValue != null) - { - // Set the first playlist item. - // This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()). - Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault()); - } - }, true); - Room.MaxAttempts.BindValueChanged(_ => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true); + + Room.PropertyChanged += onRoomPropertyChanged; + updateSetupState(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.RoomID)) + updateSetupState(); + } + + private void updateSetupState() + { + if (Room.RoomID != null) + { + // Set the first playlist item. + // This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()). + Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault()); + } } protected override Drawable CreateMainContent() => new Container @@ -92,7 +102,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, Content = new[] { - new Drawable[] + new Drawable?[] { // Playlist items column new GridContainer @@ -113,8 +123,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists AllowShowingResults = true, RequestResults = item => { - Debug.Assert(RoomId.Value != null); - ParentScreen?.Push(new PlaylistItemUserResultsScreen(null, RoomId.Value.Value, item)); + Debug.Assert(Room.RoomID != null); + ParentScreen?.Push(new PlaylistItemUserResultsScreen(null, Room.RoomID.Value, item)); } } }, @@ -248,5 +258,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { Exited = () => leaderboard.RefetchScores() }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Room.PropertyChanged -= onRoomPropertyChanged; + } } } diff --git a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs b/osu.Game/Screens/Play/RoomSubmittingPlayer.cs index 3f74f49384..74ee7e1868 100644 --- a/osu.Game/Screens/Play/RoomSubmittingPlayer.cs +++ b/osu.Game/Screens/Play/RoomSubmittingPlayer.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.Diagnostics; using osu.Game.Extensions; using osu.Game.Online.API; @@ -19,16 +17,16 @@ namespace osu.Game.Screens.Play protected readonly PlaylistItem PlaylistItem; protected readonly Room Room; - protected RoomSubmittingPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration configuration = null) + protected RoomSubmittingPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration? configuration = null) : base(configuration) { Room = room; PlaylistItem = playlistItem; } - protected override APIRequest CreateTokenRequest() + protected override APIRequest? CreateTokenRequest() { - if (!(Room.RoomID.Value is long roomId)) + if (Room.RoomID is not long roomId) return null; int beatmapId = Beatmap.Value.BeatmapInfo.OnlineID; @@ -45,8 +43,8 @@ namespace osu.Game.Screens.Play protected override APIRequest CreateSubmissionRequest(Score score, long token) { - Debug.Assert(Room.RoomID.Value != null); - return new SubmitRoomScoreRequest(score.ScoreInfo, token, Room.RoomID.Value.Value, PlaylistItem.ID); + Debug.Assert(Room.RoomID != null); + return new SubmitRoomScoreRequest(score.ScoreInfo, token, Room.RoomID.Value, PlaylistItem.ID); } } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 65a4019bb5..0c17998487 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.Multiplayer roomId = clone(roomId); password = clone(password); - ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId); + ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID == roomId); if (password != ServerAPIRoom.Password.Value) throw new InvalidOperationException("Invalid password."); diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index e9980e822c..2ce511928f 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.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 osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; @@ -17,23 +15,23 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// public partial class TestRoomManager : RoomManager { - public Action JoinRoomRequested; + public Action? JoinRoomRequested; private int currentRoomId; - public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) + public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null) { JoinRoomRequested?.Invoke(room, password); base.JoinRoom(room, password, onSuccess, onError); } - public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false, bool withSpotlightRooms = false) + public void AddRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withSpotlightRooms = false) { for (int i = 0; i < count; i++) { var room = new Room { - RoomID = { Value = -currentRoomId }, + RoomID = -currentRoomId, Name = { Value = $@"Room {currentRoomId}" }, Host = { Value = new APIUser { Username = @"Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index 41d288ba60..cc8bd27d91 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay case JoinRoomRequest joinRoomRequest: { - var room = ServerSideRooms.Single(r => r.RoomID.Value == joinRoomRequest.Room.RoomID.Value); + var room = ServerSideRooms.Single(r => r.RoomID == joinRoomRequest.Room.RoomID); if (joinRoomRequest.Password != room.Password.Value) { @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay return true; case GetRoomRequest getRoomRequest: - getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID.Value == getRoomRequest.RoomId), true)); + getRoomRequest.TriggerSuccess(createResponseRoom(ServerSideRooms.Single(r => r.RoomID == getRoomRequest.RoomId), true)); return true; case CreateRoomScoreRequest createRoomScoreRequest: @@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// The room host. public void AddServerSideRoom(Room room, APIUser host) { - room.RoomID.Value ??= currentRoomId++; + room.RoomID ??= currentRoomId++; room.Host.Value = host; for (int i = 0; i < room.Playlist.Count; i++) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index a431b204bc..ac0672c10f 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -248,7 +248,7 @@ namespace osu.Game.Users public InLobby(Room room) { - RoomID = room.RoomID.Value ?? -1; + RoomID = room.RoomID ?? -1; RoomName = room.Name.Value; } From 5b2568d18b973b2fda1ef56bfbaf0a78ad92c136 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 16:38:49 +0900 Subject: [PATCH 12/73] Fix cases where the bindable is resolved directly --- .../Multiplayer/TestSceneMatchLeaderboard.cs | 2 +- .../Match/Components/MatchLeaderboard.cs | 49 +++++++++++++------ .../Match/MultiplayerMatchSettingsOverlay.cs | 17 ++++--- .../Playlists/PlaylistsRoomSubScreen.cs | 2 +- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs index f96e0bdd4c..38522db4d4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchLeaderboard.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { SelectedRoom.Value = new Room { RoomID = 3 }; - Child = new MatchLeaderboard + Child = new MatchLeaderboard(SelectedRoom.Value) { Origin = Anchor.Centre, Anchor = Anchor.Centre, 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/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index edcad9c3ac..3e80a5531a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -323,7 +323,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, Children = new Drawable[] { - ApplyButton = new CreateOrUpdateButton + ApplyButton = new CreateOrUpdateButton(room) { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -471,13 +471,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public partial class CreateOrUpdateButton : RoundedButton { - [Resolved(typeof(Room), nameof(Room.RoomID))] - private Bindable roomId { get; set; } = null!; + private readonly Room room; - protected override void LoadComplete() + public CreateOrUpdateButton(Room room) { - base.LoadComplete(); - roomId.BindValueChanged(id => Text = id.NewValue == null ? "Create" : "Update", true); + this.room = room; } [BackgroundDependencyLoader] @@ -485,6 +483,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { BackgroundColour = colours.YellowDark; } + + protected override void Update() + { + base.Update(); + + Text = room.RoomID == null ? "Create" : "Update"; + } } private enum StartMode diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 9667e210a4..a53a89d601 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -201,7 +201,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { new OverlinedHeader("Leaderboard") }, - new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, }, + new Drawable[] { leaderboard = new MatchLeaderboard(Room) { RelativeSizeAxes = Axes.Both }, }, }, RowDimensions = new[] { From a2a930aa3592065d565f6716df2e3dea0d934ef5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 17:15:50 +0900 Subject: [PATCH 13/73] Fix CI issues --- .../Multiplayer/TestSceneLoungeRoomsContainer.cs | 6 +++--- .../Visual/Multiplayer/TestSceneMultiplayerPlayer.cs | 10 ++++------ .../OnlinePlay/Components/ListingPollingComponent.cs | 6 ++---- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index d518565be9..112fdfd9ab 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -58,14 +58,14 @@ namespace osu.Game.Tests.Visual.Multiplayer .SkipWhile(r => r.Room.Category.Value == RoomCategory.Spotlight) .All(r => r.Room.Category.Value == RoomCategory.Normal)); - AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID.Value == 0))); + AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0))); AddAssert("has 4 rooms", () => container.Rooms.Count == 4); - AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); + AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID != 0)); AddStep("select first room", () => container.Rooms.First().TriggerClick()); AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); - AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID.Value))); + AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID))); AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category.Value == RoomCategory.Spotlight))); 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/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index 4b38ea68b3..0020a7dfb6 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -35,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Components }); } - private GetRoomsRequest lastPollRequest; + private GetRoomsRequest? lastPollRequest; protected override Task Poll() { @@ -57,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Components 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); } From 2afd3579015f7b88c1aaf0aa5a8e42638729e41d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Nov 2024 13:14:35 +0900 Subject: [PATCH 14/73] Re-throw `OperationCanceledException` for consistency? Mostly to see if it breaks anything. --- osu.Game/Database/RealmArchiveModelImporter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmArchiveModelImporter.cs b/osu.Game/Database/RealmArchiveModelImporter.cs index c4eb93b754..22bcc622e9 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -139,6 +139,7 @@ namespace osu.Game.Database } catch (OperationCanceledException) { + throw; } catch (Exception e) { @@ -531,7 +532,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. From 6160df158615f16cae5e00d810ea0930132fd1e0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 16:55:18 +0900 Subject: [PATCH 15/73] Make `Room.Name` non-bindable --- .../DailyChallenge/TestSceneDailyChallenge.cs | 6 +- .../TestSceneDailyChallengeIntro.cs | 2 +- .../Visual/Menus/TestSceneMainMenu.cs | 2 +- .../Visual/Multiplayer/QueueModeTestScene.cs | 14 ++--- .../Multiplayer/TestSceneDrawableRoom.cs | 36 ++++++------ .../TestSceneDrawableRoomParticipantsList.cs | 6 +- .../Multiplayer/TestSceneMultiplayer.cs | 54 +++++++++--------- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../TestSceneMultiplayerPlaylist.cs | 2 +- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 16 +++--- .../TestScenePlaylistsMatchSettingsOverlay.cs | 26 ++++----- .../TestScenePlaylistsRoomCreation.cs | 56 +++++++------------ .../Online/Multiplayer/MultiplayerClient.cs | 4 +- osu.Game/Online/Rooms/Room.cs | 18 ++++-- .../OnlinePlay/Components/RoomManager.cs | 2 +- .../DailyChallenge/DailyChallengeIntro.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 30 +++++++++- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 22 +++++++- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 - .../Playlists/PlaylistsLoungeSubScreen.cs | 2 +- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 32 ++++++++++- .../Playlists/PlaylistsRoomSubScreen.cs | 2 +- .../Multiplayer/MultiplayerTestScene.cs | 2 +- .../Multiplayer/TestMultiplayerClient.cs | 2 +- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- osu.Game/Users/UserActivity.cs | 2 +- 29 files changed, 200 insertions(+), 153 deletions(-) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index 488289cb74..46a951dcff 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge var room = new Room { RoomID = 1234, - Name = { Value = "Daily Challenge: June 4, 2024" }, + Name = "Daily Challenge: June 4, 2024", Playlist = { new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge var room = new Room { RoomID = 1234, - Name = { Value = "Daily Challenge: June 4, 2024" }, + Name = "Daily Challenge: June 4, 2024", Playlist = { new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge var room = new Room { RoomID = 1234, - Name = { Value = "Daily Challenge: June 4, 2024" }, + Name = "Daily Challenge: June 4, 2024", Playlist = { new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs index c08398ac17..7b3595b064 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge API.Perform(new CreateRoomRequest(room = new Room { RoomID = roomId, - Name = { Value = "Daily Challenge: June 4, 2024" }, + Name = "Daily Challenge: June 4, 2024", Playlist = { new PlaylistItem(CreateAPIBeatmap(new OsuRuleset().RulesetInfo)) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 8b81516a34..9b00449e11 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Menus getRoomRequest.TriggerSuccess(new Room { RoomID = 1234, - Name = { Value = "Aug 8, 2024" }, + Name = "Aug 8, 2024", Playlist = { new PlaylistItem(beatmap) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 8bcd5aab1c..2d9d979022 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.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.Allocation; @@ -30,16 +28,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected abstract QueueMode Mode { get; } - protected BeatmapInfo InitialBeatmap { get; private set; } - protected BeatmapInfo OtherBeatmap { get; private set; } + protected BeatmapInfo InitialBeatmap { get; private set; } = null!; + protected BeatmapInfo OtherBeatmap { get; private set; } = null!; protected IScreen CurrentScreen => multiplayerComponents.CurrentScreen; protected IScreen CurrentSubScreen => multiplayerComponents.MultiplayerScreen.CurrentSubScreen; - private BeatmapManager beatmaps; - private BeatmapSetInfo importedSet; + private BeatmapManager beatmaps = null!; + private BeatmapSetInfo importedSet = null!; - private TestMultiplayerComponents multiplayerComponents; + private TestMultiplayerComponents multiplayerComponents = null!; protected TestMultiplayerClient MultiplayerClient => multiplayerComponents.MultiplayerClient; @@ -75,7 +73,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for lounge", () => multiplayerComponents.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open(new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = Mode }, Playlist = { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 4c609613a3..a386a26d32 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.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; @@ -32,12 +30,12 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); - private readonly Bindable selectedRoom = new Bindable(); + private readonly Bindable selectedRoom = new Bindable(); [Test] public void TestMultipleStatuses() { - FillFlowContainer rooms = null; + FillFlowContainer rooms = null!; AddStep("create rooms", () => { @@ -77,7 +75,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createLoungeRoom(new Room { - Name = { Value = "Multiplayer room" }, + Name = "Multiplayer room", Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = { Value = MatchType.HeadToHead }, @@ -86,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }), createLoungeRoom(new Room { - Name = { Value = "Private room" }, + Name = "Private room", Status = { Value = new RoomStatusOpenPrivate() }, HasPassword = { Value = true }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, @@ -96,7 +94,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }), createLoungeRoom(new Room { - Name = { Value = "Playlist room with multiple beatmaps" }, + Name = "Playlist room with multiple beatmaps", Status = { Value = new RoomStatusPlaying() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Playlist = { item1, item2 }, @@ -104,19 +102,19 @@ namespace osu.Game.Tests.Visual.Multiplayer }), createLoungeRoom(new Room { - Name = { Value = "Finished room" }, + Name = "Finished room", Status = { Value = new RoomStatusEnded() }, EndDate = { Value = DateTimeOffset.Now }, }), createLoungeRoom(new Room { - Name = { Value = "Spotlight room" }, + Name = "Spotlight room", Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.Spotlight }, }), createLoungeRoom(new Room { - Name = { Value = "Featured artist room" }, + Name = "Featured artist room", Status = { Value = new RoomStatusOpen() }, Category = { Value = RoomCategory.FeaturedArtist }, }), @@ -132,12 +130,12 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestEnableAndDisablePassword() { - DrawableRoom drawableRoom = null; - Room room = null; + DrawableRoom drawableRoom = null!; + Room room = null!; AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room { - Name = { Value = "Room with password" }, + Name = "Room with password", Status = { Value = new RoomStatusOpen() }, Type = { Value = MatchType.HeadToHead }, })); @@ -166,30 +164,30 @@ namespace osu.Game.Tests.Visual.Multiplayer { new DrawableMatchRoom(new Room { - Name = { Value = "A host-only room" }, + Name = "A host-only room", QueueMode = { Value = QueueMode.HostOnly }, Type = { Value = MatchType.HeadToHead }, }) { - SelectedItem = new Bindable() + SelectedItem = new Bindable() }, new DrawableMatchRoom(new Room { - Name = { Value = "An all-players, team-versus room" }, + Name = "An all-players, team-versus room", QueueMode = { Value = QueueMode.AllPlayers }, Type = { Value = MatchType.TeamVersus } }) { - SelectedItem = new Bindable() + SelectedItem = new Bindable() }, new DrawableMatchRoom(new Room { - Name = { Value = "A round-robin room" }, + Name = "A round-robin room", QueueMode = { Value = QueueMode.AllPlayersRoundRobin }, Type = { Value = MatchType.HeadToHead } }) { - SelectedItem = new Bindable() + SelectedItem = new Bindable() }, } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 98abc93994..c959c07ffb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.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; @@ -17,7 +15,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene { - private DrawableRoomParticipantsList list; + private DrawableRoomParticipantsList list = null!; public override void SetUpSteps() { @@ -27,7 +25,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { SelectedRoom.Value = new Room { - Name = { Value = "test room" }, + Name = "test room", Host = { Value = new APIUser diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index df2021dbaf..0b662083b5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -103,7 +103,7 @@ 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) @@ -238,7 +238,7 @@ 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) @@ -259,7 +259,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { roomManager.AddServerSideRoom(new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -288,7 +288,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { roomManager.AddServerSideRoom(new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -317,7 +317,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Password = { Value = "password" }, Playlist = { @@ -338,7 +338,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { roomManager.AddServerSideRoom(new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Password = { Value = "password" }, Playlist = { @@ -370,7 +370,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Password = { Value = "password" }, Playlist = { @@ -401,7 +401,7 @@ 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) @@ -430,7 +430,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }; return new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Playlist = { item } }; }); @@ -471,7 +471,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }; return new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Playlist = { item } }; }); @@ -512,7 +512,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }; return new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Playlist = { item } }; }); @@ -548,7 +548,7 @@ 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) @@ -581,7 +581,7 @@ 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) @@ -620,7 +620,7 @@ 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) @@ -639,7 +639,7 @@ 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) @@ -679,7 +679,7 @@ 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) @@ -724,7 +724,7 @@ 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) @@ -754,7 +754,7 @@ 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) @@ -791,7 +791,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { roomManager.AddServerSideRoom(new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { @@ -810,7 +810,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("disable polling", () => this.ChildrenOfType().Single().TimeBetweenPolls.Value = 0); AddStep("change server-side settings", () => { - roomManager.ServerSideRooms[0].Name.Value = "New name"; + roomManager.ServerSideRooms[0].Name = "New name"; roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { ID = 2, @@ -825,7 +825,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("local room has correct settings", () => { var localRoom = this.ChildrenOfType().Single().Room; - return localRoom.Name.Value == roomManager.ServerSideRooms[0].Name.Value + return localRoom.Name == roomManager.ServerSideRooms[0].Name && localRoom.Playlist.SequenceEqual(roomManager.ServerSideRooms[0].Playlist); }); } @@ -836,7 +836,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { @@ -872,7 +872,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { @@ -911,7 +911,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { @@ -942,7 +942,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { @@ -976,7 +976,7 @@ 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) @@ -1022,7 +1022,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 9bf29c7bf8..0c2a657c5a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("load match", () => { - SelectedRoom.Value = new Room { Name = { Value = "Test Room" } }; + SelectedRoom.Value = new Room { Name = "Test Room" }; LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value)); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index e12c2bee49..92b44d9502 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -164,7 +164,7 @@ 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) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 32e90153d8..7c30b72747 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,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Type = { Value = MatchType.TeamVersus }, Playlist = { @@ -84,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Type = { Value = MatchType.TeamVersus }, Playlist = { @@ -121,7 +119,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createRoom(() => new Room { - Name = { Value = "Test Room" }, + Name = "Test Room", Type = { Value = MatchType.HeadToHead }, Playlist = { @@ -147,7 +145,7 @@ 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) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 9f7b20ad43..3652710e32 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using NUnit.Framework; using osu.Framework.Bindables; @@ -22,7 +20,7 @@ namespace osu.Game.Tests.Visual.Playlists { protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; - private TestRoomSettings settings; + private TestRoomSettings settings = null!; protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies(); @@ -47,19 +45,19 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("clear name and beatmap", () => { - SelectedRoom.Value.Name.Value = ""; + SelectedRoom.Value.Name = ""; SelectedRoom.Value.Playlist.Clear(); }); 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))); 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,7 +67,7 @@ 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", () => { @@ -85,7 +83,7 @@ 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 name", () => createdRoom.Name == expected_name); AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration); } @@ -94,13 +92,13 @@ 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.Name = "Test Room"; SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap)); errorMessage = $"{not_found_prefix} {beatmap.OnlineID}"; @@ -127,7 +125,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("setup", () => { - SelectedRoom.Value.Name.Value = "Test Room"; + SelectedRoom.Value.Name = "Test Room"; SelectedRoom.Value.Playlist.Add(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/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 1636a3d4b8..8769ff2e27 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,7 +60,7 @@ namespace osu.Game.Tests.Visual.Playlists { setupAndCreateRoom(room => { - room.Name.Value = "my awesome room"; + room.Name = "my awesome room"; room.Host.Value = API.LocalUser.Value; room.RecentParticipants.Add(room.Host.Value); room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); @@ -88,7 +83,7 @@ namespace osu.Game.Tests.Visual.Playlists { setupAndCreateRoom(room => { - room.Name.Value = "my awesome room"; + room.Name = "my awesome room"; room.MaxAttempts.Value = 5; room.Host.Value = API.LocalUser.Value; room.RecentParticipants.Add(room.Host.Value); @@ -107,7 +102,7 @@ namespace osu.Game.Tests.Visual.Playlists { setupAndCreateRoom(room => { - room.Name.Value = "my awesome room"; + room.Name = "my awesome room"; room.Host.Value = API.LocalUser.Value; room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { @@ -115,13 +110,13 @@ namespace osu.Game.Tests.Visual.Playlists }); }); - AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom!.Value.Playlist[0]); + AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]); } [Test] public void TestBeatmapUpdatedOnReImport() { - string realHash = null; + string realHash = null!; int realOnlineId = 0; int realOnlineSetId = 0; @@ -139,26 +134,23 @@ 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.Name = "my awesome room"; room.Host.Value = API.LocalUser.Value; room.Playlist.Add(new PlaylistItem(new BeatmapInfo { @@ -181,17 +173,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 +187,7 @@ namespace osu.Game.Tests.Visual.Playlists private void setupAndCreateRoom(Action room) { - AddStep("setup room", () => room(SelectedRoom!.Value)); + AddStep("setup room", () => room(SelectedRoom.Value)); AddStep("click create button", () => { @@ -215,8 +201,7 @@ namespace osu.Game.Tests.Visual.Playlists var beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); Debug.Assert(beatmap.BeatmapInfo.BeatmapSet != null); - - importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)?.Value.Detach(); + importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)!.Value.Detach(); }); private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen @@ -226,8 +211,7 @@ namespace osu.Game.Tests.Visual.Playlists public new Bindable Beatmap => base.Beatmap; [Resolved(canBeNull: true)] - [CanBeNull] - private IDialogOverlay dialogOverlay { get; set; } + private IDialogOverlay? dialogOverlay { get; set; } public TestPlaylistsRoomSubScreen(Room room) : base(room) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bd0fa0cb72..b6ad361c44 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -459,7 +459,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 = () => { @@ -841,7 +841,7 @@ namespace osu.Game.Online.Multiplayer // Update a few properties of the room instantaneously. Room.Settings = settings; - APIRoom.Name.Value = Room.Settings.Name; + APIRoom.Name = 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; diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index de854596e9..d2aa8ee4fe 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -30,6 +30,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref roomId, value); } + /// + /// The room name. + /// + public string Name + { + get => name; + set => SetField(ref name, value); + } + /// /// Represents the current item selected within the room. /// @@ -45,13 +54,12 @@ namespace osu.Game.Online.Rooms [JsonProperty("id")] private long? roomId; + [JsonProperty("name")] + private string name = string.Empty; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; - [Cached] - [JsonProperty("name")] - public readonly Bindable Name = new Bindable(); - [Cached] [JsonProperty("host")] public readonly Bindable Host = new Bindable(); @@ -205,7 +213,7 @@ namespace osu.Game.Online.Rooms public void CopyFrom(Room other) { RoomID = other.RoomID; - Name.Value = other.Name.Value; + Name = other.Name; Category.Value = other.Category.Value; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 555a1f66bd..0bb33df966 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -122,7 +122,7 @@ 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); rooms.Remove(room); 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/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 59c8047062..e1235ab41e 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; @@ -26,6 +27,7 @@ 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 { @@ -47,6 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private RoomSpecialCategoryPill? specialCategoryPill; private PasswordProtectedIcon? passwordIcon; private EndDateInfo? endDateInfo; + private SpriteText? roomName; private UpdateableBeatmapBackgroundSprite background = null!; private DelayedLoadWrapper wrapper = null!; @@ -181,11 +184,10 @@ 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 { @@ -247,6 +249,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); + Room.PropertyChanged += onRoomPropertyChanged; + wrapper.DelayedLoadComplete += _ => { Debug.Assert(specialCategoryPill != null); @@ -272,11 +276,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hasPassword.BindTo(Room.HasPassword); hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); + + updateRoomName(); }; SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true); } + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Name)) + updateRoomName(); + } + + private void updateRoomName() + { + if (roomName != null) + roomName.Text = Room.Name; + } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) @@ -332,6 +350,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components return pills; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + Room.PropertyChanged -= onRoomPropertyChanged; + } + private partial class RoomStatusText : OnlinePlayComposite { public readonly IBindable SelectedItem = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 36db1ab29b..a277b3771b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -121,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; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 3e80a5531a..3dc01cee54 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -350,7 +350,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); - RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); @@ -374,8 +373,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match drawablePlaylist.Items.BindTo(Playlist); drawablePlaylist.SelectedItem.BindTo(SelectedItem); + + room.PropertyChanged += onRoomPropertyChanged; + + updateRoomName(); } + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Name)) + updateRoomName(); + } + + private void updateRoomName() + => NameField.Text = room.Name; + protected override void Update() { base.Update(); @@ -417,7 +429,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } else { - room.Name.Value = NameField.Text; + room.Name = NameField.Text; room.Type.Value = TypePicker.Current.Value; room.Password.Value = PasswordTextBox.Current.Value; room.QueueMode.Value = QueueModeDropdown.Current.Value; @@ -467,6 +479,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match applyingSettingsOperation.Dispose(); applyingSettingsOperation = null; }); + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } public partial class CreateOrUpdateButton : RoundedButton diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index a3a6fd2d8e..1ccd02ba8d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Room CreateNewRoom() => new Room { - Name = { Value = $"{api.LocalUser}'s awesome room" }, + Name = $"{api.LocalUser}'s awesome room", Type = { Value = MatchType.HeadToHead }, }; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 696eb58c3f..60c2a586f9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSubScreen(Room room) : base(room) { - Title = room.RoomID == null ? "New room" : room.Name.Value; + Title = room.RoomID == null ? "New room" : room.Name; Activity.Value = new UserActivity.InLobby(room); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 9911a08b81..f642b9c989 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -16,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay /// public partial class OnlinePlayComposite : CompositeDrawable { - [Resolved(typeof(Room), nameof(Room.Name))] - protected Bindable RoomName { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable Host { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index e1d747c3b0..a35d114418 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { return new Room { - Name = { Value = $"{api.LocalUser}'s awesome playlist" }, + Name = $"{api.LocalUser}'s awesome playlist", Type = { Value = MatchType.Playlists } }; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 4d85206473..84a4657693 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using Humanizer; using Humanizer.Localisation; @@ -25,6 +26,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components; using osuTK; using osu.Game.Localisation; using osu.Game.Rulesets; +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Screens.OnlinePlay.Playlists { @@ -142,7 +144,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - LengthLimit = 100 + LengthLimit = 100, + Text = room.Name }, }, new Section("Duration") @@ -313,7 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists loadingLayer = new LoadingLayer(true) }; - RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true); Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); @@ -337,6 +339,24 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Playlist.BindCollectionChanged(onPlaylistChanged, true); } + protected override void LoadComplete() + { + base.LoadComplete(); + + room.PropertyChanged += onRoomPropertyChanged; + + updateRoomName(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Name)) + updateRoomName(); + } + + private void updateRoomName() + => NameField.Text = room.Name; + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -384,7 +404,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists hideError(); - RoomName.Value = NameField.Text; + room.Name = NameField.Text; Availability.Value = AvailabilityPicker.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) @@ -436,6 +456,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists ErrorText.FadeIn(50); loadingLayer.Hide(); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } public partial class CreateRoomButton : RoundedButton diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index a53a89d601..9780c48f1f 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists public PlaylistsRoomSubScreen(Room room) : base(room, false) // Editing is temporarily not allowed. { - Title = room.RoomID == null ? "New playlist" : room.Name.Value; + Title = room.RoomID == null ? "New playlist" : room.Name; Activity.Value = new UserActivity.InLobby(room); } diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 80c69db8b1..e540c78b0f 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { return new Room { - Name = { Value = "test name" }, + Name = "test name", Type = { Value = MatchType.HeadToHead }, Playlist = { diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 0c17998487..7a8e553452 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Settings = { - Name = ServerAPIRoom.Name.Value, + Name = ServerAPIRoom.Name, MatchType = ServerAPIRoom.Type.Value, Password = password ?? string.Empty, QueueMode = ServerAPIRoom.QueueMode.Value, diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index 2ce511928f..d8c110d12a 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay var room = new Room { RoomID = -currentRoomId, - Name = { Value = $@"Room {currentRoomId}" }, + Name = $@"Room {currentRoomId}", Host = { Value = new APIUser { Username = @"Host" } }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, Category = { Value = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index ac0672c10f..93812e3f6b 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -249,7 +249,7 @@ namespace osu.Game.Users public InLobby(Room room) { RoomID = room.RoomID ?? -1; - RoomName = room.Name.Value; + RoomName = room.Name; } [SerializationConstructor] From 8694f7e1cc2dca48df8458fed22f433c2a7cdfa9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 17:32:32 +0900 Subject: [PATCH 16/73] Make `Room.Host` non-bindable --- .../Multiplayer/TestSceneDrawableRoom.cs | 2 +- .../TestSceneDrawableRoomParticipantsList.cs | 11 ++-- .../TestScenePlaylistsRoomCreation.cs | 12 ++--- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 17 ++++--- .../OnlinePlay/Components/RoomManager.cs | 2 +- .../Lounge/Components/DrawableRoom.cs | 2 +- .../DrawableRoomParticipantsList.cs | 51 ++++++++++++------- .../OnlinePlay/Match/DrawableMatchRoom.cs | 24 +++++++-- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- .../OnlinePlay/TestRoomRequestsHandler.cs | 4 +- 12 files changed, 80 insertions(+), 52 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index a386a26d32..17c0a159e9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -195,7 +195,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private DrawableRoom createLoungeRoom(Room room) { - room.Host.Value ??= new APIUser { Username = "peppy", Id = 2 }; + room.Host ??= new APIUser { Username = "peppy", Id = 2 }; if (room.RecentParticipants.Count == 0) { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index c959c07ffb..1e7003c612 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -26,17 +26,14 @@ namespace osu.Game.Tests.Visual.Multiplayer SelectedRoom.Value = new Room { Name = "test room", - Host = + Host = new APIUser { - Value = new APIUser - { - Id = 2, - Username = "peppy", - } + Id = 2, + Username = "peppy", } }; - Child = list = new DrawableRoomParticipantsList + Child = list = new DrawableRoomParticipantsList(SelectedRoom.Value) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 8769ff2e27..7cc86e71bf 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -61,8 +61,8 @@ namespace osu.Game.Tests.Visual.Playlists setupAndCreateRoom(room => { room.Name = "my awesome room"; - room.Host.Value = API.LocalUser.Value; - room.RecentParticipants.Add(room.Host.Value); + room.Host = API.LocalUser.Value; + room.RecentParticipants.Add(room.Host); room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { @@ -85,8 +85,8 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name = "my awesome room"; room.MaxAttempts.Value = 5; - room.Host.Value = API.LocalUser.Value; - room.RecentParticipants.Add(room.Host.Value); + room.Host = API.LocalUser.Value; + room.RecentParticipants.Add(room.Host); room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Playlists setupAndCreateRoom(room => { room.Name = "my awesome room"; - room.Host.Value = API.LocalUser.Value; + room.Host = API.LocalUser.Value; room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.Playlists setupAndCreateRoom(room => { room.Name = "my awesome room"; - room.Host.Value = API.LocalUser.Value; + room.Host = API.LocalUser.Value; room.Playlist.Add(new PlaylistItem(new BeatmapInfo { MD5Hash = realHash, diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index b6ad361c44..0d2b18a483 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -528,7 +528,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); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index d2aa8ee4fe..48282ba5e9 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -39,6 +39,12 @@ namespace osu.Game.Online.Rooms set => SetField(ref name, value); } + public APIUser? Host + { + get => host; + set => SetField(ref host, value); + } + /// /// Represents the current item selected within the room. /// @@ -57,13 +63,12 @@ namespace osu.Game.Online.Rooms [JsonProperty("name")] private string name = string.Empty; + [JsonProperty("host")] + private APIUser? host; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; - [Cached] - [JsonProperty("host")] - public readonly Bindable Host = new Bindable(); - [Cached] [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); @@ -217,8 +222,8 @@ namespace osu.Game.Online.Rooms Category.Value = other.Category.Value; - 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; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 0bb33df966..ca42e98e3c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay.Components 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); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index e1235ab41e..99809a2a50 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -224,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, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 60e05285d9..2ffe740e24 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.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.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; 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 +14,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 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; } @@ -172,7 +172,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components totalCount.Text = ParticipantCount.Value.ToString(); }, true); - Host.BindValueChanged(onHostChanged, true); + room.PropertyChanged += onRoomPropertyChanged; + updateRoomHost(); } private int numberOfCircles = 4; @@ -199,7 +200,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private void onParticipantsChanged(object sender, NotifyCollectionChangedEventArgs e) + private void onParticipantsChanged(object? sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { @@ -269,21 +270,33 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - private void onHostChanged(ValueChangedEvent host) + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - hostAvatar.User = host.NewValue; + if (e.PropertyName == nameof(Room.Host)) + updateRoomHost(); + } + + 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); } } + 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/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index e5d3bd1586..0c993f4abf 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -2,12 +2,12 @@ // 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.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; @@ -30,7 +30,6 @@ namespace osu.Game.Screens.OnlinePlay.Match private IAPIProvider api { get; set; } = null!; private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); - private readonly IBindable host = new Bindable(); private readonly bool allowEdit; private Drawable? editButton; @@ -40,7 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Match this.allowEdit = allowEdit; base.SelectedItem.BindTo(SelectedItem); - host.BindTo(room.Host); } [BackgroundDependencyLoader] @@ -62,13 +60,31 @@ namespace osu.Game.Screens.OnlinePlay.Match { base.LoadComplete(); + Room.PropertyChanged += onRoomPropertyChanged; + updateRoomHost(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Host)) + updateRoomHost(); + } + + private void updateRoomHost() + { if (editButton != null) - host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); + 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/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index f642b9c989..4c614bba8f 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -16,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay /// public partial class OnlinePlayComposite : CompositeDrawable { - [Resolved(typeof(Room))] - protected Bindable Host { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable Status { get; private set; } = null!; diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index d8c110d12a..b0f91ca0b9 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay { RoomID = -currentRoomId, Name = $@"Room {currentRoomId}", - Host = { Value = new APIUser { Username = @"Host" } }, + Host = new APIUser { Username = @"Host" }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, Category = { Value = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, }; diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index cc8bd27d91..271b036d8e 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -262,12 +262,12 @@ namespace osu.Game.Tests.Visual.OnlinePlay public void AddServerSideRoom(Room room, APIUser host) { room.RoomID ??= currentRoomId++; - room.Host.Value = host; + room.Host = host; for (int i = 0; i < room.Playlist.Count; i++) { room.Playlist[i].ID = currentPlaylistItemId++; - room.Playlist[i].OwnerID = room.Host.Value.OnlineID; + room.Playlist[i].OwnerID = room.Host.OnlineID; } serverSideRooms.Add(room); From bde7b8e610d9f72a33cf05a6d2ae208e4daf3f58 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 18:27:32 +0900 Subject: [PATCH 17/73] Make `Room.Category` non-bindable --- .../DailyChallenge/TestSceneDailyChallenge.cs | 6 ++-- .../TestSceneDailyChallengeIntro.cs | 2 +- .../Multiplayer/TestSceneDrawableRoom.cs | 4 +-- .../TestSceneLoungeRoomsContainer.cs | 10 +++--- osu.Game/Online/Rooms/Room.cs | 30 +++++++++------- .../Components/ListingPollingComponent.cs | 2 +- .../Components/StatusColouredContainer.cs | 12 +++---- .../Lounge/Components/DrawableRoom.cs | 32 ++++++++++------- .../Components/RoomSpecialCategoryPill.cs | 34 ++++++++++++++++--- .../Lounge/Components/RoomsContainer.cs | 2 +- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- 13 files changed, 86 insertions(+), 55 deletions(-) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index 46a951dcff..e05f5f13ae 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge } }, EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, - Category = { Value = RoomCategory.DailyChallenge } + Category = RoomCategory.DailyChallenge }; AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge } }, EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, - Category = { Value = RoomCategory.DailyChallenge } + Category = RoomCategory.DailyChallenge }; AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge } }, EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, - Category = { Value = RoomCategory.DailyChallenge } + Category = RoomCategory.DailyChallenge }; AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs index 7b3595b064..33be952e20 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge }, StartDate = { Value = DateTimeOffset.Now }, EndDate = { Value = DateTimeOffset.Now.AddHours(24) }, - Category = { Value = RoomCategory.DailyChallenge } + Category = RoomCategory.DailyChallenge })); }); AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = roomId })); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 17c0a159e9..07e557fe36 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -110,13 +110,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Spotlight room", Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.Spotlight }, + Category = RoomCategory.Spotlight, }), createLoungeRoom(new Room { Name = "Featured artist room", Status = { Value = new RoomStatusOpen() }, - Category = { Value = RoomCategory.FeaturedArtist }, + Category = RoomCategory.FeaturedArtist, }), } }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 112fdfd9ab..8654fe1f11 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -55,20 +55,20 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("has 5 rooms", () => container.Rooms.Count == 5); AddAssert("all spotlights at top", () => container.Rooms - .SkipWhile(r => r.Room.Category.Value == RoomCategory.Spotlight) - .All(r => r.Room.Category.Value == RoomCategory.Normal)); + .SkipWhile(r => r.Room.Category == RoomCategory.Spotlight) + .All(r => r.Room.Category == RoomCategory.Normal)); AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0))); AddAssert("has 4 rooms", () => container.Rooms.Count == 4); AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID != 0)); AddStep("select first room", () => container.Rooms.First().TriggerClick()); - AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); + AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight))); AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID))); - AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight))); + AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight))); - AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category.Value == RoomCategory.Spotlight))); + AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight))); AddAssert("selection vacated", () => checkRoomSelected(null)); } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 48282ba5e9..1a7a3f73e8 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -39,12 +39,24 @@ namespace osu.Game.Online.Rooms set => SetField(ref name, 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); + } + /// /// Represents the current item selected within the room. /// @@ -66,6 +78,10 @@ namespace osu.Game.Online.Rooms [JsonProperty("host")] private APIUser? host; + [JsonProperty("category")] + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] + private RoomCategory category; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -85,18 +101,6 @@ namespace osu.Game.Online.Rooms [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 - { - get => Category.Value; - set => Category.Value = value; - } - [Cached] public readonly Bindable MaxAttempts = new Bindable(); @@ -220,7 +224,7 @@ namespace osu.Game.Online.Rooms RoomID = other.RoomID; Name = other.Name; - Category.Value = other.Category.Value; + Category = other.Category; if (other.Host != null && Host?.Id != other.Host.Id) Host = other.Host; diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index 0020a7dfb6..67c3586a35 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -51,7 +51,7 @@ 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()) { diff --git a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs index ed39021a73..90127dc961 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.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.Bindables; using osu.Framework.Graphics; @@ -17,13 +15,13 @@ namespace osu.Game.Screens.OnlinePlay.Components private readonly double transitionDuration; [Resolved(typeof(Room), nameof(Room.Status))] - private Bindable status { get; set; } + private Bindable status { get; set; } = null!; - [Resolved(typeof(Room), nameof(Room.Category))] - private Bindable category { get; set; } + private readonly Room room; - public StatusColouredContainer(double transitionDuration = 100) + public StatusColouredContainer(Room room, double transitionDuration = 100) { + this.room = room; this.transitionDuration = transitionDuration; } @@ -32,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { status.BindValueChanged(s => { - this.FadeColour(colours.ForRoomCategory(category.Value) ?? s.NewValue.GetAppropriateColour(colours), transitionDuration); + this.FadeColour(colours.ForRoomCategory(room.Category) ?? s.NewValue.GetAppropriateColour(colours), transitionDuration); }, true); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 99809a2a50..8b2f2bfe4a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -164,7 +164,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft }, - specialCategoryPill = new RoomSpecialCategoryPill + specialCategoryPill = new RoomSpecialCategoryPill(Room) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft @@ -259,15 +259,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components 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 => { @@ -278,6 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); updateRoomName(); + updateRoomCategory(); }; SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true); @@ -285,8 +277,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Name)) - updateRoomName(); + switch (e.PropertyName) + { + case nameof(Room.Name): + updateRoomName(); + break; + + case nameof(Room.Category): + updateRoomCategory(); + break; + } } private void updateRoomName() @@ -295,6 +295,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomName.Text = Room.Name; } + private void updateRoomCategory() + { + if (Room.Category > RoomCategory.Normal) + specialCategoryPill?.Show(); + else + specialCategoryPill?.Hide(); + } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) 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/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 3d9b7a46d0..f197a7d018 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -169,7 +169,7 @@ 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 ?? 0)); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index a277b3771b..1fc177ce0b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -66,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 diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 4c614bba8f..0a51dcc8eb 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -31,9 +31,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable DifficultyRange { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable Category { get; private set; } = null!; - [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index b0f91ca0b9..07dd57eb68 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay Name = $@"Room {currentRoomId}", Host = new APIUser { Username = @"Host" }, EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, - Category = { Value = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }, + Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal, }; if (withPassword) From 81e4cb348fe4f39b4ff1198857845694e99c7f55 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 18:36:47 +0900 Subject: [PATCH 18/73] Make `Room.Type` non-bindable --- .../Multiplayer/TestSceneDrawableRoom.cs | 12 ++++---- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 10 +++---- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 27 ++++++++--------- .../Lounge/Components/DrawableRoom.cs | 29 ++++++++++++------- .../Lounge/Components/MatchTypePill.cs | 28 +++++++++++++++--- .../Match/MultiplayerMatchSettingsOverlay.cs | 19 +++++++++--- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Playlists/PlaylistsLoungeSubScreen.cs | 8 ++--- .../Multiplayer/MultiplayerTestScene.cs | 4 +-- .../Multiplayer/TestMultiplayerClient.cs | 2 +- 12 files changed, 89 insertions(+), 57 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 07e557fe36..4c9aa6af15 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Multiplayer room", Status = { Value = new RoomStatusOpen() }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, Playlist = { item1 }, CurrentPlaylistItem = item1 }), @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Status = { Value = new RoomStatusOpenPrivate() }, HasPassword = { Value = true }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, Playlist = { item3 }, CurrentPlaylistItem = item3 }), @@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Room with password", Status = { Value = new RoomStatusOpen() }, - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, })); AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType().Any()); @@ -166,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "A host-only room", QueueMode = { Value = QueueMode.HostOnly }, - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, }) { SelectedItem = new Bindable() @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "An all-players, team-versus room", QueueMode = { Value = QueueMode.AllPlayers }, - Type = { Value = MatchType.TeamVersus } + Type = MatchType.TeamVersus }) { SelectedItem = new Bindable() @@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "A round-robin room", QueueMode = { Value = QueueMode.AllPlayersRoundRobin }, - Type = { Value = MatchType.HeadToHead } + Type = MatchType.HeadToHead }) { SelectedItem = new Bindable() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 7c30b72747..b68089015e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - Type = { Value = MatchType.TeamVersus }, + Type = MatchType.TeamVersus, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - Type = { Value = MatchType.TeamVersus }, + Type = MatchType.TeamVersus, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -130,14 +130,14 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - 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] diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 0d2b18a483..a6037e8907 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -844,7 +844,7 @@ namespace osu.Game.Online.Multiplayer APIRoom.Name = 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.Type = Room.Settings.MatchType; APIRoom.QueueMode.Value = Room.Settings.QueueMode; APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 1a7a3f73e8..acc7d7b3df 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -57,6 +57,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref category, value); } + /// + /// The match type. + /// + public MatchType Type + { + get => type; + set => SetField(ref type, value); + } + /// /// Represents the current item selected within the room. /// @@ -82,6 +91,10 @@ namespace osu.Game.Online.Rooms [JsonConverter(typeof(SnakeCaseStringEnumConverter))] private RoomCategory category; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] + [JsonProperty("type")] + private MatchType type; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -110,18 +123,6 @@ namespace osu.Game.Online.Rooms [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 - { - get => Type.Value; - set => Type.Value = value; - } - [Cached] public readonly Bindable QueueMode = new Bindable(); @@ -233,7 +234,7 @@ namespace osu.Game.Online.Rooms Status.Value = other.Status.Value; Availability.Value = other.Availability.Value; HasPassword.Value = other.HasPassword.Value; - Type.Value = other.Type.Value; + Type = other.Type; MaxParticipants.Value = other.MaxParticipants.Value; ParticipantCount.Value = other.ParticipantCount.Value; EndDate.Value = other.EndDate.Value; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 8b2f2bfe4a..d05d887038 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -189,7 +189,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: 28) }, - new RoomStatusText + new RoomStatusText(Room) { SelectedItem = { BindTarget = SelectedItem } } @@ -259,17 +259,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components wrapper.FadeInFromZero(200); - 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(); }; SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true); @@ -286,6 +281,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components case nameof(Room.Category): updateRoomCategory(); break; + + case nameof(Room.Type): + updateRoomType(); + break; } } @@ -303,6 +302,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components specialCategoryPill?.Hide(); } + private void updateRoomType() + { + if (endDateInfo != null) + endDateInfo.Alpha = Room.Type == MatchType.Playlists ? 1 : 0; + } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) @@ -331,11 +336,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { var pills = new List(); - if (Room.Type.Value != MatchType.Playlists) + if (Room.Type != MatchType.Playlists) { pills.AddRange(new OnlinePlayComposite[] { - new MatchTypePill(), + new MatchTypePill(Room), new QueueModePill(), }); } @@ -374,11 +379,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private BeatmapLookupCache beatmapLookupCache { get; set; } = null!; + private readonly Room room; private SpriteText statusText = null!; private LinkFlowContainer beatmapText = null!; - public RoomStatusText() + public RoomStatusText(Room room) { + this.room = room; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; } @@ -437,7 +444,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components 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/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/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 3dc01cee54..226484e9a5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -350,7 +350,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); - Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true); @@ -377,17 +376,29 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.PropertyChanged += onRoomPropertyChanged; updateRoomName(); + updateRoomType(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Name)) - updateRoomName(); + switch (e.PropertyName) + { + case nameof(Room.Name): + updateRoomName(); + break; + + case nameof(Room.Type): + updateRoomName(); + break; + } } private void updateRoomName() => NameField.Text = room.Name; + private void updateRoomType() + => TypePicker.Current.Value = room.Type; + protected override void Update() { base.Update(); @@ -430,7 +441,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else { room.Name = NameField.Text; - room.Type.Value = TypePicker.Current.Value; + room.Type = TypePicker.Current.Value; room.Password.Value = PasswordTextBox.Current.Value; room.QueueMode.Value = QueueModeDropdown.Current.Value; room.AutoStartDuration.Value = autoStartDuration; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 1ccd02ba8d..a5cc267569 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Room CreateNewRoom() => new Room { Name = $"{api.LocalUser}'s awesome room", - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, }; protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 0a51dcc8eb..7e29c2f117 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -19,9 +19,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Status { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable Type { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable PlaylistItemStats { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index a35d114418..d66b4f844c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -22,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists public partial class PlaylistsLoungeSubScreen : LoungeSubScreen { [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; - private Dropdown categoryDropdown; + private Dropdown categoryDropdown = null!; protected override IEnumerable CreateFilterControls() { @@ -68,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return new Room { Name = $"{api.LocalUser}'s awesome playlist", - Type = { Value = MatchType.Playlists } + Type = MatchType.Playlists }; } diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index e540c78b0f..774b1d5931 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.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.Game.Online.Rooms; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual.OnlinePlay; @@ -38,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new Room { Name = "test name", - Type = { Value = MatchType.HeadToHead }, + Type = MatchType.HeadToHead, Playlist = { new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 7a8e553452..6111ba0a6d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -231,7 +231,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Settings = { Name = ServerAPIRoom.Name, - MatchType = ServerAPIRoom.Type.Value, + MatchType = ServerAPIRoom.Type, Password = password ?? string.Empty, QueueMode = ServerAPIRoom.QueueMode.Value, AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value From 5d4838a08b93cd4ae5d4d963710b6e679fdada69 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 18:48:13 +0900 Subject: [PATCH 19/73] Make `Room.Status` non-bindable --- .../Multiplayer/TestSceneDrawableRoom.cs | 14 +++---- .../Online/Multiplayer/MultiplayerClient.cs | 8 ++-- osu.Game/Online/Rooms/GetRoomsRequest.cs | 6 +-- osu.Game/Online/Rooms/Room.cs | 19 ++++++--- .../Components/StatusColouredContainer.cs | 39 +++++++++++++------ .../Lounge/Components/DrawableRoom.cs | 2 +- .../Lounge/Components/RoomStatusPill.cs | 35 +++++++++++++---- .../Multiplayer/MultiplayerRoomManager.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- 9 files changed, 84 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 4c9aa6af15..a40a75c885 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createLoungeRoom(new Room { Name = "Multiplayer room", - Status = { Value = new RoomStatusOpen() }, + Status = new RoomStatusOpen(), EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = MatchType.HeadToHead, Playlist = { item1 }, @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createLoungeRoom(new Room { Name = "Private room", - Status = { Value = new RoomStatusOpenPrivate() }, + Status = new RoomStatusOpenPrivate(), HasPassword = { Value = true }, EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = MatchType.HeadToHead, @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createLoungeRoom(new Room { Name = "Playlist room with multiple beatmaps", - Status = { Value = new RoomStatusPlaying() }, + Status = new RoomStatusPlaying(), EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Playlist = { item1, item2 }, CurrentPlaylistItem = item1 @@ -103,19 +103,19 @@ namespace osu.Game.Tests.Visual.Multiplayer createLoungeRoom(new Room { Name = "Finished room", - Status = { Value = new RoomStatusEnded() }, + Status = new RoomStatusEnded(), EndDate = { Value = DateTimeOffset.Now }, }), createLoungeRoom(new Room { Name = "Spotlight room", - Status = { Value = new RoomStatusOpen() }, + Status = new RoomStatusOpen(), Category = RoomCategory.Spotlight, }), createLoungeRoom(new Room { Name = "Featured artist room", - Status = { Value = new RoomStatusOpen() }, + Status = new RoomStatusOpen(), Category = RoomCategory.FeaturedArtist, }), } @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room { Name = "Room with password", - Status = { Value = new RoomStatusOpen() }, + Status = new RoomStatusOpen(), Type = MatchType.HeadToHead, })); diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a6037e8907..f26dbb3239 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -397,15 +397,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.Value ? 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; } @@ -843,7 +843,7 @@ namespace osu.Game.Online.Multiplayer Room.Settings = settings; APIRoom.Name = Room.Settings.Name; APIRoom.Password.Value = Room.Settings.Password; - APIRoom.Status.Value = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate(); + APIRoom.Status = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate(); APIRoom.Type = Room.Settings.MatchType; APIRoom.QueueMode.Value = Room.Settings.QueueMode; APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 1b5e08c729..980ce74718 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -45,11 +45,11 @@ namespace osu.Game.Online.Rooms foreach (var room in Response) { if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value) - room.Status.Value = new RoomStatusEnded(); + room.Status = new RoomStatusEnded(); else if (room.HasPassword.Value) - room.Status.Value = new RoomStatusOpenPrivate(); + room.Status = new RoomStatusOpenPrivate(); else - room.Status.Value = new RoomStatusOpen(); + room.Status = new RoomStatusOpen(); } } } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index acc7d7b3df..760580c003 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -78,6 +78,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref currentPlaylistItem, value); } + /// + /// The current room status. + /// + public RoomStatus Status + { + get => status; + set => SetField(ref status, value); + } + [JsonProperty("id")] private long? roomId; @@ -98,6 +107,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; + // Not serialised (see: GetRoomsRequest). + private RoomStatus status = new RoomStatusOpen(); + [Cached] [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); @@ -117,9 +129,6 @@ namespace osu.Game.Online.Rooms [Cached] public readonly Bindable MaxAttempts = new Bindable(); - [Cached] - public readonly Bindable Status = new Bindable(new RoomStatusOpen()); - [Cached] public readonly Bindable Availability = new Bindable(); @@ -231,7 +240,7 @@ namespace osu.Game.Online.Rooms Host = other.Host; ChannelId.Value = other.ChannelId.Value; - Status.Value = other.Status.Value; + Status = other.Status; Availability.Value = other.Availability.Value; HasPassword.Value = other.HasPassword.Value; Type = other.Type; @@ -266,7 +275,7 @@ 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)) + if (Status is not RoomStatusEnded) Playlist.RemoveAll(i => i.Expired); } diff --git a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs index 90127dc961..2b1233506f 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StatusColouredContainer.cs @@ -1,22 +1,21 @@ // 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.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; - - [Resolved(typeof(Room), nameof(Room.Status))] - private Bindable status { get; set; } = null!; - private readonly Room room; public StatusColouredContainer(Room room, double transitionDuration = 100) @@ -25,13 +24,29 @@ namespace osu.Game.Screens.OnlinePlay.Components this.transitionDuration = transitionDuration; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void LoadComplete() { - status.BindValueChanged(s => - { - this.FadeColour(colours.ForRoomCategory(room.Category) ?? 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/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index d05d887038..8fb439452c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -159,7 +159,7 @@ 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 diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 96d698a184..10d3695d14 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,43 @@ 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; + + EndDate.BindValueChanged(_ => updateDisplay()); + room.PropertyChanged += onRoomPropertyChanged; + updateDisplay(); + + FinishTransforms(true); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Status)) + updateDisplay(); } 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/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index d7efd30435..b119c6d59d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join. // should probably be done at a higher level, but due to the current structure of things this is the easiest place for now. - if (room.Status.Value is RoomStatusEnded) + if (room.Status is RoomStatusEnded) { onError?.Invoke("Cannot join an ended room."); return; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 7e29c2f117..82774abac7 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -16,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay /// public partial class OnlinePlayComposite : CompositeDrawable { - [Resolved(typeof(Room))] - protected Bindable Status { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable PlaylistItemStats { get; private set; } = null!; From 7e3e5208f0c719993f7bcc46809069979287509e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 18:52:04 +0900 Subject: [PATCH 20/73] Make `Room.Availability` non-bindable --- osu.Game/Online/Rooms/Room.cs | 17 +++++++++++++---- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 --- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 19 +++++++++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 760580c003..d43d40bd66 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -87,6 +87,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref status, value); } + /// + /// Describes which players are able to join the room. + /// + public RoomAvailability Availability + { + get => availability; + set => SetField(ref availability, value); + } + [JsonProperty("id")] private long? roomId; @@ -110,6 +119,9 @@ namespace osu.Game.Online.Rooms // Not serialised (see: GetRoomsRequest). private RoomStatus status = new RoomStatusOpen(); + // Not yet serialised (not implemented). + private RoomAvailability availability; + [Cached] [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); @@ -129,9 +141,6 @@ namespace osu.Game.Online.Rooms [Cached] public readonly Bindable MaxAttempts = new Bindable(); - [Cached] - public readonly Bindable Availability = new Bindable(); - [Cached] public readonly Bindable QueueMode = new Bindable(); @@ -241,7 +250,7 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status = other.Status; - Availability.Value = other.Availability.Value; + Availability = other.Availability; HasPassword.Value = other.HasPassword.Value; Type = other.Type; MaxParticipants.Value = other.MaxParticipants.Value; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 82774abac7..d444067da6 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -46,9 +46,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable EndDate { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable Availability { get; private set; } = null!; - [Resolved(typeof(Room))] public Bindable Password { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 84a4657693..aee26e544a 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -316,7 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists loadingLayer = new LoadingLayer(true) }; - Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); @@ -346,17 +345,29 @@ namespace osu.Game.Screens.OnlinePlay.Playlists room.PropertyChanged += onRoomPropertyChanged; updateRoomName(); + updateRoomAvailability(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Name)) - updateRoomName(); + switch (e.PropertyName) + { + case nameof(Room.Name): + updateRoomName(); + break; + + case nameof(Room.Availability): + updateRoomAvailability(); + break; + } } private void updateRoomName() => NameField.Text = room.Name; + private void updateRoomAvailability() + => AvailabilityPicker.Current.Value = room.Availability; + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -405,7 +416,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists hideError(); room.Name = NameField.Text; - Availability.Value = AvailabilityPicker.Current.Value; + room.Availability = AvailabilityPicker.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) MaxParticipants.Value = max; From 198681e64495bace774d65ebec02bddeba0ffc62 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 18:57:51 +0900 Subject: [PATCH 21/73] Make `Room.QueueMode` non-bindable --- .../Visual/Multiplayer/QueueModeTestScene.cs | 4 +-- .../Multiplayer/TestSceneDrawableRoom.cs | 6 ++-- .../Multiplayer/TestSceneHostOnlyQueueMode.cs | 2 +- .../Multiplayer/TestSceneMultiplayer.cs | 12 ++++---- .../TestSceneMultiplayerQueueList.cs | 8 +++--- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 27 ++++++++++-------- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Lounge/Components/QueueModePill.cs | 28 +++++++++++++++---- .../Match/MultiplayerMatchSettingsOverlay.cs | 11 ++++++-- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 4 --- .../Multiplayer/TestMultiplayerClient.cs | 2 +- 13 files changed, 67 insertions(+), 43 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 2d9d979022..7dce7daf6d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open(new Room { Name = "Test Room", - QueueMode = { Value = Mode }, + QueueMode = Mode, Playlist = { new PlaylistItem(InitialBeatmap) @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestCreatedWithCorrectMode() { - AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode); + AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode == Mode); } protected void RunGameplay() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index a40a75c885..79ac0fa7b6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Visual.Multiplayer new DrawableMatchRoom(new Room { Name = "A host-only room", - QueueMode = { Value = QueueMode.HostOnly }, + QueueMode = QueueMode.HostOnly, Type = MatchType.HeadToHead, }) { @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.Multiplayer new DrawableMatchRoom(new Room { Name = "An all-players, team-versus room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Type = MatchType.TeamVersus }) { @@ -183,7 +183,7 @@ namespace osu.Game.Tests.Visual.Multiplayer new DrawableMatchRoom(new Room { Name = "A round-robin room", - QueueMode = { Value = QueueMode.AllPlayersRoundRobin }, + QueueMode = QueueMode.AllPlayersRoundRobin, Type = MatchType.HeadToHead }) { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index 78baa4a39b..e0364fd65d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = QueueMode.AllPlayers }).WaitSafely()); - AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers); + AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 0b662083b5..2ce6e5a577 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -792,7 +792,7 @@ namespace osu.Game.Tests.Visual.Multiplayer roomManager.AddServerSideRoom(new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -837,7 +837,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -873,7 +873,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -912,7 +912,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -943,7 +943,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -1023,7 +1023,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - QueueMode = { Value = QueueMode.AllPlayers }, + QueueMode = QueueMode.AllPlayers, Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index 47fb4e06ea..10625d57aa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -69,7 +69,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 +81,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 +100,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 +109,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/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index f26dbb3239..f36c53204d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -845,7 +845,7 @@ namespace osu.Game.Online.Multiplayer APIRoom.Password.Value = Room.Settings.Password; APIRoom.Status = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate(); APIRoom.Type = Room.Settings.MatchType; - APIRoom.QueueMode.Value = Room.Settings.QueueMode; + APIRoom.QueueMode = Room.Settings.QueueMode; APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId); APIRoom.AutoSkip.Value = Room.Settings.AutoSkip; diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index d43d40bd66..b85b59e992 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -66,6 +66,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref type, value); } + /// + /// The playlist queueing mode. Only valid for multiplayer rooms. + /// + public QueueMode QueueMode + { + get => queueMode; + set => SetField(ref queueMode, value); + } + /// /// Represents the current item selected within the room. /// @@ -96,6 +105,7 @@ namespace osu.Game.Online.Rooms set => SetField(ref availability, value); } + [JsonProperty("id")] private long? roomId; @@ -113,6 +123,10 @@ namespace osu.Game.Online.Rooms [JsonProperty("type")] private MatchType type; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] + [JsonProperty("queue_mode")] + private QueueMode queueMode; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -141,17 +155,6 @@ namespace osu.Game.Online.Rooms [Cached] public readonly Bindable MaxAttempts = new Bindable(); - [Cached] - public readonly Bindable QueueMode = new Bindable(); - - [JsonConverter(typeof(SnakeCaseStringEnumConverter))] - [JsonProperty("queue_mode")] - private QueueMode queueMode - { - get => QueueMode.Value; - set => QueueMode.Value = value; - } - [Cached] public readonly Bindable AutoStartDuration = new Bindable(); @@ -257,7 +260,7 @@ namespace osu.Game.Online.Rooms ParticipantCount.Value = other.ParticipantCount.Value; EndDate.Value = other.EndDate.Value; UserScore.Value = other.UserScore.Value; - QueueMode.Value = other.QueueMode.Value; + QueueMode = other.QueueMode; AutoStartDuration.Value = other.AutoStartDuration.Value; DifficultyRange.Value = other.DifficultyRange.Value; PlaylistItemStats.Value = other.PlaylistItemStats.Value; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 8fb439452c..f5e72f4b99 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -341,7 +341,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components pills.AddRange(new OnlinePlayComposite[] { new MatchTypePill(Room), - new QueueModePill(), + new QueueModePill(Room), }); } 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/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 226484e9a5..36bdb97e9e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -352,7 +352,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); - QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true); @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomName(); updateRoomType(); + updateRoomQueueMode(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -390,6 +390,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.Type): updateRoomName(); break; + + case nameof(Room.QueueMode): + updateRoomQueueMode(); + break; } } @@ -399,6 +403,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomType() => TypePicker.Current.Value = room.Type; + private void updateRoomQueueMode() + => QueueModeDropdown.Current.Value = room.QueueMode; + protected override void Update() { base.Update(); @@ -443,7 +450,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.Name = NameField.Text; room.Type = TypePicker.Current.Value; room.Password.Value = PasswordTextBox.Current.Value; - room.QueueMode.Value = QueueModeDropdown.Current.Value; + room.QueueMode = QueueModeDropdown.Current.Value; room.AutoStartDuration.Value = autoStartDuration; room.AutoSkip.Value = AutoSkipCheckbox.Current.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 60c2a586f9..8f2f3aa678 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -358,7 +358,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Activity.Value = new UserActivity.InLobby(Room); } - private bool localUserCanAddItem => client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly; + private bool localUserCanAddItem => client.IsHost || Room.QueueMode != QueueMode.HostOnly; private void updateCurrentItem() { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index d444067da6..22e9e72f19 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay @@ -52,9 +51,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable QueueMode { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable AutoStartDuration { get; private set; } = null!; diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 6111ba0a6d..7f110ae6a5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -233,7 +233,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = ServerAPIRoom.Name, MatchType = ServerAPIRoom.Type, Password = password ?? string.Empty, - QueueMode = ServerAPIRoom.QueueMode.Value, + QueueMode = ServerAPIRoom.QueueMode, AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value }, Playlist = ServerAPIRoom.Playlist.Select(CreateMultiplayerPlaylistItem).ToList(), From ec5be6dbc3719bc42ff6eeb9f6ffd54aa530b776 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 19:11:05 +0900 Subject: [PATCH 22/73] Make `Room.Password` & `Room.HasPassword` non-bindable --- .../TestSceneDrawableLoungeRoom.cs | 2 +- .../Multiplayer/TestSceneDrawableRoom.cs | 6 +-- .../TestSceneLoungeRoomsContainer.cs | 4 +- .../Multiplayer/TestSceneMultiplayer.cs | 10 ++-- .../Online/Multiplayer/MultiplayerClient.cs | 6 +-- osu.Game/Online/Rooms/GetRoomsRequest.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 49 ++++++++++++++----- .../Lounge/Components/DrawableRoom.cs | 14 ++++-- .../Lounge/Components/RoomsContainer.cs | 4 +- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 11 ++++- .../Multiplayer/MultiplayerRoomManager.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Multiplayer/TestMultiplayerClient.cs | 2 +- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- .../OnlinePlay/TestRoomRequestsHandler.cs | 9 ++-- 16 files changed, 80 insertions(+), 48 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs index 9fe7189a0a..c5fb52461a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { private readonly Room room = new Room { - HasPassword = { Value = true } + Password = "*" }; [Cached] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 79ac0fa7b6..b5cfecfa25 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Private room", Status = new RoomStatusOpenPrivate(), - HasPassword = { Value = true }, + Password = "*", EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Type = MatchType.HeadToHead, Playlist = { item3 }, @@ -144,10 +144,10 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); - AddStep("set password", () => room.Password.Value = "password"); + AddStep("set password", () => room.Password = "password"); AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType().Single().Alpha)); - AddStep("unset password", () => room.Password.Value = string.Empty); + AddStep("unset password", () => room.Password = string.Empty); AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha)); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 8654fe1f11..e28790f2ed 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -182,11 +182,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public }); - AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword.Value)); + AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword)); AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private }); - AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword.Value)); + AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword)); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 2ce6e5a577..6e9c18f040 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -318,7 +318,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - Password = { Value = "password" }, + Password = "password", Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password"); + AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password == "password"); } [Test] @@ -339,7 +339,7 @@ namespace osu.Game.Tests.Visual.Multiplayer roomManager.AddServerSideRoom(new Room { Name = "Test Room", - Password = { Value = "password" }, + Password = "password", Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -371,7 +371,7 @@ namespace osu.Game.Tests.Visual.Multiplayer createRoom(() => new Room { Name = "Test Room", - Password = { Value = "password" }, + Password = "password", Playlist = { new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) @@ -382,7 +382,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }); AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2")); - AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password2"); + AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password == "password2"); } [Test] diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index f36c53204d..db35f228fe 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -184,7 +184,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(room.RoomID != null); // Join the server-side room. - var joinedRoom = await JoinRoom(room.RoomID.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. @@ -397,7 +397,7 @@ namespace osu.Game.Online.Multiplayer switch (state) { case MultiplayerRoomState.Open: - APIRoom.Status = APIRoom.HasPassword.Value ? new RoomStatusOpenPrivate() : new RoomStatusOpen(); + APIRoom.Status = APIRoom.HasPassword ? new RoomStatusOpenPrivate() : new RoomStatusOpen(); break; case MultiplayerRoomState.Playing: @@ -842,7 +842,7 @@ namespace osu.Game.Online.Multiplayer // Update a few properties of the room instantaneously. Room.Settings = settings; APIRoom.Name = Room.Settings.Name; - APIRoom.Password.Value = Room.Settings.Password; + 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; diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 980ce74718..0c8df642fa 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -46,7 +46,7 @@ namespace osu.Game.Online.Rooms { if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value) room.Status = new RoomStatusEnded(); - else if (room.HasPassword.Value) + else if (room.HasPassword) room.Status = new RoomStatusOpenPrivate(); else room.Status = new RoomStatusOpen(); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b85b59e992..3f50611372 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -39,6 +39,35 @@ namespace osu.Game.Online.Rooms set => SetField(ref name, value); } + /// + /// 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 => password; + set + { + SetField(ref password, value); + HasPassword = !string.IsNullOrEmpty(value); + } + } + + /// + /// 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. /// @@ -112,6 +141,12 @@ namespace osu.Game.Online.Rooms [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; @@ -172,9 +207,6 @@ namespace osu.Game.Online.Rooms [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(); @@ -185,10 +217,6 @@ namespace osu.Game.Online.Rooms #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(); @@ -229,11 +257,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("auto_skip")] public readonly Bindable AutoSkip = new Bindable(); - public Room() - { - Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue)); - } - /// /// Copies values from another into this one. /// @@ -254,7 +277,7 @@ namespace osu.Game.Online.Rooms ChannelId.Value = other.ChannelId.Value; Status = other.Status; Availability = other.Availability; - HasPassword.Value = other.HasPassword.Value; + HasPassword = other.HasPassword; Type = other.Type; MaxParticipants.Value = other.MaxParticipants.Value; ParticipantCount.Value = other.ParticipantCount.Value; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index f5e72f4b99..390ea06c1b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -259,12 +259,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components wrapper.FadeInFromZero(200); - 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); @@ -285,6 +283,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components case nameof(Room.Type): updateRoomType(); break; + + case nameof(Room.HasPassword): + updateRoomHasPassword(); + break; } } @@ -308,6 +310,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components endDateInfo.Alpha = Room.Type == MatchType.Playlists ? 1 : 0; } + private void updateRoomHasPassword() + { + if (passwordIcon != null) + passwordIcon.Alpha = Room.HasPassword ? 1 : 0; + } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index f197a7d018..11ad886773 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -101,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"); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 1fc177ce0b..d396d18b4f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -185,7 +185,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge return true; } - if (Room.HasPassword.Value) + if (Room.HasPassword) { this.ShowPopover(); return true; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 36bdb97e9e..a3441fbd09 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -351,7 +351,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); - Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true); @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomName(); updateRoomType(); updateRoomQueueMode(); + updateRoomPassword(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -394,6 +394,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.QueueMode): updateRoomQueueMode(); break; + + case nameof(Room.Password): + updateRoomPassword(); + break; } } @@ -406,6 +410,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomQueueMode() => QueueModeDropdown.Current.Value = room.QueueMode; + private void updateRoomPassword() + => PasswordTextBox.Text = room.Password ?? string.Empty; + protected override void Update() { base.Update(); @@ -449,7 +456,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { room.Name = NameField.Text; room.Type = TypePicker.Current.Value; - room.Password.Value = PasswordTextBox.Current.Value; + room.Password = PasswordTextBox.Current.Value; room.QueueMode = QueueModeDropdown.Current.Value; room.AutoStartDuration.Value = autoStartDuration; room.AutoSkip.Value = AutoSkipCheckbox.Current.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index b119c6d59d..e16582a6e1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerClient multiplayerClient { get; set; } = null!; public override void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null) - => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError); + => base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password, onSuccess, onError), onError); public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null) { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 22e9e72f19..93abc23e55 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -45,9 +45,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable EndDate { get; private set; } = null!; - [Resolved(typeof(Room))] - public Bindable Password { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } = null!; diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 7f110ae6a5..dcb6f2503d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ServerAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID == roomId); - if (password != ServerAPIRoom.Password.Value) + if (password != ServerAPIRoom.Password) throw new InvalidOperationException("Invalid password."); lastPlaylistItemId = ServerAPIRoom.Playlist.Max(item => item.ID); diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index 07dd57eb68..3db9e12ab7 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay }; if (withPassword) - room.Password.Value = @"password"; + room.Password = @"password"; if (ruleset != null) { diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index 271b036d8e..0934c4f3dc 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -51,8 +51,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay var apiRoom = cloneRoom(createRoomRequest.Room); // Passwords are explicitly not copied between rooms. - apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value); - apiRoom.Password.Value = createRoomRequest.Room.Password.Value; + apiRoom.Password = createRoomRequest.Room.Password; AddServerSideRoom(apiRoom, localUser); @@ -66,7 +65,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay { var room = ServerSideRooms.Single(r => r.RoomID == joinRoomRequest.Room.RoomID); - if (joinRoomRequest.Password != room.Password.Value) + if (joinRoomRequest.Password != room.Password) { joinRoomRequest.TriggerFailure(new InvalidOperationException("Invalid password.")); return true; @@ -278,9 +277,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay var responseRoom = cloneRoom(room); // Password is hidden from the response, and is only propagated via HasPassword. - bool hadPassword = responseRoom.HasPassword.Value; - responseRoom.Password.Value = null; - responseRoom.HasPassword.Value = hadPassword; + responseRoom.Password = responseRoom.HasPassword ? Guid.NewGuid().ToString() : null; if (!withParticipants) responseRoom.RecentParticipants.Clear(); From f001cce24adcea7dfce18d8a1ce41bd8f494f660 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 19:15:29 +0900 Subject: [PATCH 23/73] Make `Room.AutoSkip` non-bindable --- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 19 +++++++++++++------ .../Match/MultiplayerMatchSettingsOverlay.cs | 11 +++++++++-- .../Multiplayer/MultiplayerPlayer.cs | 4 ++-- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 --- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index db35f228fe..a9c0108661 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -848,7 +848,7 @@ namespace osu.Game.Online.Multiplayer APIRoom.QueueMode = Room.Settings.QueueMode; APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId); - APIRoom.AutoSkip.Value = Room.Settings.AutoSkip; + APIRoom.AutoSkip = Room.Settings.AutoSkip; RoomUpdated?.Invoke(); } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 3f50611372..b945d1b886 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -104,6 +104,15 @@ namespace osu.Game.Online.Rooms 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); + } + /// /// Represents the current item selected within the room. /// @@ -134,7 +143,6 @@ namespace osu.Game.Online.Rooms set => SetField(ref availability, value); } - [JsonProperty("id")] private long? roomId; @@ -162,6 +170,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("queue_mode")] private QueueMode queueMode; + [JsonProperty("auto_skip")] + private bool autoSkip; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -253,10 +264,6 @@ namespace osu.Game.Online.Rooms set => MaxAttempts.Value = value; } - [Cached] - [JsonProperty("auto_skip")] - public readonly Bindable AutoSkip = new Bindable(); - /// /// Copies values from another into this one. /// @@ -288,7 +295,7 @@ namespace osu.Game.Online.Rooms DifficultyRange.Value = other.DifficultyRange.Value; PlaylistItemStats.Value = other.PlaylistItemStats.Value; CurrentPlaylistItem = other.CurrentPlaylistItem; - AutoSkip.Value = other.AutoSkip.Value; + AutoSkip = other.AutoSkip; other.RemoveExpiredPlaylistItems(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index a3441fbd09..62d6459487 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -352,7 +352,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); - AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); operationInProgress.BindValueChanged(v => @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomType(); updateRoomQueueMode(); updateRoomPassword(); + updateRoomAutoSkip(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -398,6 +398,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.Password): updateRoomPassword(); break; + + case nameof(Room.AutoSkip): + updateRoomAutoSkip(); + break; } } @@ -413,6 +417,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomPassword() => PasswordTextBox.Text = room.Password ?? string.Empty; + private void updateRoomAutoSkip() + => AutoSkipCheckbox.Current.Value = room.AutoSkip; + protected override void Update() { base.Update(); @@ -459,7 +466,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.Password = PasswordTextBox.Current.Value; room.QueueMode = QueueModeDropdown.Current.Value; room.AutoStartDuration.Value = autoStartDuration; - room.AutoSkip.Value = AutoSkipCheckbox.Current.Value; + room.AutoSkip = AutoSkipCheckbox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) room.MaxParticipants.Value = max; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index d444af2fd3..c058699b0e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -50,8 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AllowPause = false, AllowRestart = false, AllowFailAnimation = false, - AllowSkipping = room.AutoSkip.Value, - AutomaticallySkipIntro = room.AutoSkip.Value, + AllowSkipping = room.AutoSkip, + AutomaticallySkipIntro = room.AutoSkip, AlwaysShowLeaderboard = true, }) { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 93abc23e55..2b1c9cf00a 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -50,8 +50,5 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable AutoStartDuration { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable AutoSkip { get; private set; } = null!; } } From b8bae30b66b6f171191314b13d0810e6529373f9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 19:45:03 +0900 Subject: [PATCH 24/73] Make `Room.ParticipantCount` & `Room.MaxParticipants` non-bindable --- .../TestSceneDrawableRoomParticipantsList.cs | 4 +- .../Multiplayer/TestSceneMultiplayer.cs | 4 +- .../TestScenePlaylistsParticipantsList.cs | 4 +- .../Online/Multiplayer/MultiplayerClient.cs | 4 +- osu.Game/Online/Rooms/Room.cs | 35 +++++++++--- .../Components/ParticipantCountDisplay.cs | 55 +++++++++++++++---- .../Components/ParticipantsDisplay.cs | 40 +++++++++++--- .../DrawableRoomParticipantsList.cs | 27 ++++++--- .../Match/MultiplayerMatchSettingsOverlay.cs | 13 ++++- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 6 -- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 15 +++-- 11 files changed, 146 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 1e7003c612..3d0c1be22c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -143,13 +143,13 @@ namespace osu.Game.Tests.Visual.Multiplayer Id = id, Username = $"User {id}" }); - SelectedRoom.Value.ParticipantCount.Value++; + SelectedRoom.Value.ParticipantCount++; } private void removeUserAt(int index) { SelectedRoom.Value.RecentParticipants.RemoveAt(index); - SelectedRoom.Value.ParticipantCount.Value--; + SelectedRoom.Value.ParticipantCount--; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 6e9c18f040..e625d23779 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -248,7 +248,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1); AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } @@ -308,7 +308,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => multiplayerClient.RoomJoined); - AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1); + AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1); AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index b9e5875e06..2368a2068d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -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/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index a9c0108661..3a9f728b41 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -492,7 +492,7 @@ namespace osu.Game.Online.Multiplayer Id = user.UserID, Username = "[Unresolved]" }); - APIRoom.ParticipantCount.Value++; + APIRoom.ParticipantCount++; } private Task handleUserLeft(MultiplayerRoomUser user, Action? callback) @@ -507,7 +507,7 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(APIRoom != null); APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID); - APIRoom.ParticipantCount.Value--; + APIRoom.ParticipantCount--; callback?.Invoke(user); RoomUpdated?.Invoke(); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b945d1b886..b63ebd107d 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -86,6 +86,24 @@ namespace osu.Game.Online.Rooms set => SetField(ref category, 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 match type. /// @@ -162,6 +180,12 @@ namespace osu.Game.Online.Rooms [JsonConverter(typeof(SnakeCaseStringEnumConverter))] private RoomCategory category; + // Not yet serialised (not implemented). + private int? maxParticipants; + + [JsonProperty("participant_count")] + private int participantCount; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] [JsonProperty("type")] private MatchType type; @@ -211,9 +235,6 @@ namespace osu.Game.Online.Rooms set => AutoStartDuration.Value = TimeSpan.FromSeconds(value); } - [Cached] - public readonly Bindable MaxParticipants = new Bindable(); - [Cached] [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); @@ -222,10 +243,6 @@ namespace osu.Game.Online.Rooms [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] @@ -286,8 +303,8 @@ namespace osu.Game.Online.Rooms Availability = other.Availability; HasPassword = other.HasPassword; Type = other.Type; - MaxParticipants.Value = other.MaxParticipants.Value; - ParticipantCount.Value = other.ParticipantCount.Value; + MaxParticipants = other.MaxParticipants; + ParticipantCount = other.ParticipantCount; EndDate.Value = other.EndDate.Value; UserScore.Value = other.UserScore.Value; QueueMode = other.QueueMode; diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs index 9f7e700ab3..f9744717d5 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.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.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 { @@ -16,18 +16,21 @@ namespace osu.Game.Screens.OnlinePlay.Components 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..1ae2756514 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -1,19 +1,23 @@ // 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.Game.Graphics.Containers; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { public partial class ParticipantsDisplay : OnlinePlayComposite { - 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; @@ -46,14 +50,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/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 2ffe740e24..06ae939842 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -166,14 +166,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; + updateRoomHost(); + updateRoomParticipantCount(); } private int numberOfCircles = 4; @@ -257,7 +254,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { int hiddenCount = 0; if (RecentParticipants.Count > NumberOfCircles) - hiddenCount = ParticipantCount.Value - NumberOfCircles + 1; + hiddenCount = room.ParticipantCount - NumberOfCircles + 1; hiddenUsers.Count = hiddenCount; @@ -272,8 +269,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Host)) - updateRoomHost(); + switch (e.PropertyName) + { + case nameof(Room.Host): + updateRoomHost(); + break; + + case nameof(Room.ParticipantCount): + updateRoomParticipantCount(); + break; + } } private void updateRoomHost() @@ -288,6 +293,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } + private void updateRoomParticipantCount() + { + updateHiddenUsers(); + totalCount.Text = room.ParticipantCount.ToString(); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 62d6459487..141bddc4d4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -350,7 +350,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); - MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomQueueMode(); updateRoomPassword(); updateRoomAutoSkip(); + updateRoomMaxParticipants(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -402,6 +402,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.AutoSkip): updateRoomAutoSkip(); break; + + case nameof(Room.MaxParticipants): + updateRoomMaxParticipants(); + break; } } @@ -420,6 +424,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomAutoSkip() => AutoSkipCheckbox.Current.Value = room.AutoSkip; + private void updateRoomMaxParticipants() + => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + protected override void Update() { base.Update(); @@ -469,9 +476,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.AutoSkip = AutoSkipCheckbox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) - room.MaxParticipants.Value = max; + room.MaxParticipants = max; else - room.MaxParticipants.Value = null; + room.MaxParticipants = null; manager.CreateRoom(room, onSuccess, onError); } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 2b1c9cf00a..7623cbabbd 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -27,12 +27,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable ParticipantCount { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable MaxParticipants { get; private set; } = null!; - [Resolved(typeof(Room))] protected Bindable MaxAttempts { get; private set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index aee26e544a..20fbe42dd5 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -316,7 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists loadingLayer = new LoadingLayer(true) }; - MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); @@ -346,6 +345,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists updateRoomName(); updateRoomAvailability(); + updateRoomMaxParticipants(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -359,6 +359,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.Availability): updateRoomAvailability(); break; + + case nameof(Room.MaxParticipants): + updateRoomMaxParticipants(); + break; } } @@ -368,6 +372,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomAvailability() => AvailabilityPicker.Current.Value = room.Availability; + private void updateRoomMaxParticipants() + => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -417,11 +424,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists room.Name = NameField.Text; room.Availability = AvailabilityPicker.Current.Value; - - if (int.TryParse(MaxParticipantsField.Text, out int max)) - MaxParticipants.Value = max; - else - MaxParticipants.Value = null; + room.MaxParticipants = int.TryParse(MaxParticipantsField.Text, out int max) ? max : null; if (int.TryParse(MaxAttemptsField.Text, out max)) MaxAttempts.Value = max; From 89de4f0f87a81feae6714b8bea2b85fd36bf7a35 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 19:53:21 +0900 Subject: [PATCH 25/73] Make `Room.AutoStartDuration` non-bindable --- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 24 ++++++++++--------- .../Match/MultiplayerMatchSettingsOverlay.cs | 15 ++++++++---- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 --- .../Multiplayer/TestMultiplayerClient.cs | 2 +- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 3a9f728b41..6a7d460922 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -846,7 +846,7 @@ namespace osu.Game.Online.Multiplayer APIRoom.Status = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate(); APIRoom.Type = Room.Settings.MatchType; APIRoom.QueueMode = Room.Settings.QueueMode; - APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration; + APIRoom.AutoStartDuration = Room.Settings.AutoStartDuration; APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId); APIRoom.AutoSkip = Room.Settings.AutoSkip; diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index b63ebd107d..61f944a728 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -131,6 +131,15 @@ namespace osu.Game.Online.Rooms 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); + } + /// /// Represents the current item selected within the room. /// @@ -197,6 +206,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("auto_skip")] private bool autoSkip; + [JsonProperty("auto_start_duration")] + private ushort autoStartDuration; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -225,16 +237,6 @@ namespace osu.Game.Online.Rooms [Cached] public readonly Bindable MaxAttempts = new Bindable(); - [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] [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); @@ -308,7 +310,7 @@ namespace osu.Game.Online.Rooms EndDate.Value = other.EndDate.Value; UserScore.Value = other.UserScore.Value; QueueMode = other.QueueMode; - AutoStartDuration.Value = other.AutoStartDuration.Value; + AutoStartDuration = other.AutoStartDuration; DifficultyRange.Value = other.DifficultyRange.Value; PlaylistItemStats.Value = other.PlaylistItemStats.Value; CurrentPlaylistItem = other.CurrentPlaylistItem; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index 141bddc4d4..f883fde600 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -350,7 +350,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true); - AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true); operationInProgress.BindTo(ongoingOperationTracker.InProgress); operationInProgress.BindValueChanged(v => @@ -377,6 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomPassword(); updateRoomAutoSkip(); updateRoomMaxParticipants(); + updateRoomAutoStartDuration(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -406,6 +406,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.MaxParticipants): updateRoomMaxParticipants(); break; + + case nameof(Room.AutoStartDuration): + updateRoomAutoStartDuration(); + break; } } @@ -427,6 +431,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomMaxParticipants() => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + private void updateRoomAutoStartDuration() + => typeLabel.Text = room.AutoStartDuration.GetLocalisableDescription(); + protected override void Update() { base.Update(); @@ -445,8 +452,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Debug.Assert(applyingSettingsOperation == null); applyingSettingsOperation = ongoingOperationTracker.BeginOperation(); - TimeSpan autoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value); - // If the client is already in a room, update via the client. // Otherwise, update the room directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) @@ -456,7 +461,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match password: PasswordTextBox.Text, matchType: TypePicker.Current.Value, queueMode: QueueModeDropdown.Current.Value, - autoStartDuration: autoStartDuration, + autoStartDuration: TimeSpan.FromSeconds((int)startModeDropdown.Current.Value), autoSkip: AutoSkipCheckbox.Current.Value) .ContinueWith(t => Schedule(() => { @@ -472,7 +477,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match room.Type = TypePicker.Current.Value; room.Password = PasswordTextBox.Current.Value; room.QueueMode = QueueModeDropdown.Current.Value; - room.AutoStartDuration.Value = autoStartDuration; + room.AutoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value); room.AutoSkip = AutoSkipCheckbox.Current.Value; if (int.TryParse(MaxParticipantsField.Text, out int max)) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 7623cbabbd..70418d1a7e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -41,8 +41,5 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable AutoStartDuration { get; private set; } = null!; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index dcb6f2503d..29e62cb590 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -234,7 +234,7 @@ namespace osu.Game.Tests.Visual.Multiplayer MatchType = ServerAPIRoom.Type, Password = password ?? string.Empty, QueueMode = ServerAPIRoom.QueueMode, - AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value + AutoStartDuration = ServerAPIRoom.AutoStartDuration }, Playlist = ServerAPIRoom.Playlist.Select(CreateMultiplayerPlaylistItem).ToList(), Users = { localUser }, From 0ceaafe7318ac0b73c12dc746623b63e02de8e7e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 20:20:14 +0900 Subject: [PATCH 26/73] Make `Room.Duration` & `Room.StartDate` & `Room.EndDate` non-bindable --- .../DailyChallenge/TestSceneDailyChallenge.cs | 6 +- .../TestSceneDailyChallengeCarousel.cs | 6 +- .../TestSceneDailyChallengeIntro.cs | 4 +- ...estSceneDailyChallengeTimeRemainingRing.cs | 22 +++--- .../Visual/Menus/TestSceneMainMenu.cs | 4 +- .../Multiplayer/TestSceneDrawableRoom.cs | 8 +- .../TestScenePlaylistsMatchSettingsOverlay.cs | 2 +- .../TestScenePlaylistsRoomCreation.cs | 4 +- .../UserInterface/TestSceneMainMenuButton.cs | 8 +- .../Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Rooms/GetRoomsRequest.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 74 +++++++++++-------- osu.Game/Screens/Menu/DailyChallengeButton.cs | 6 +- .../DailyChallenge/DailyChallenge.cs | 4 +- .../DailyChallengeTimeRemainingRing.cs | 40 ++++++++-- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Lounge/Components/EndDateInfo.cs | 51 +++++++++---- .../Lounge/Components/RoomStatusPill.cs | 10 ++- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 4 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 10 --- .../Playlists/PlaylistsReadyButton.cs | 18 ++--- .../Playlists/PlaylistsRoomFooter.cs | 9 +-- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 11 ++- .../Playlists/PlaylistsRoomSubScreen.cs | 2 +- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- 25 files changed, 186 insertions(+), 125 deletions(-) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index e05f5f13ae..1a19c423fc 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge AllowedMods = [new APIMod(new OsuModDoubleTime())] } }, - EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, + EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge AllowedMods = [new APIMod(new OsuModDoubleTime())] } }, - EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, + EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; @@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge AllowedMods = [new APIMod(new OsuModDoubleTime())] } }, - EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, + EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs index d53e386ad4..5854c2e793 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge Origin = Anchor.Centre, Children = new Drawable[] { - new DailyChallengeTimeRemainingRing(), + new DailyChallengeTimeRemainingRing(room.Value), breakdown = new DailyChallengeScoreBreakdown(), } } @@ -125,8 +125,8 @@ namespace osu.Game.Tests.Visual.DailyChallenge AddSliderStep("update time remaining", 0f, 1f, 0f, progress => { var startedTimeAgo = TimeSpan.FromHours(24) * progress; - room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo; - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now - startedTimeAgo; + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); AddStep("add normal score", () => { diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs index 33be952e20..aa311766d5 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs @@ -78,8 +78,8 @@ namespace osu.Game.Tests.Visual.DailyChallenge AllowedMods = [new APIMod(new OsuModDoubleTime())] } }, - StartDate = { Value = DateTimeOffset.Now }, - EndDate = { Value = DateTimeOffset.Now.AddHours(24) }, + StartDate = DateTimeOffset.Now, + EndDate = DateTimeOffset.Now.AddHours(24), Category = RoomCategory.DailyChallenge })); }); diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs index baa1eb8318..2f522d4d6f 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background4, }, - ring = new DailyChallengeTimeRemainingRing + ring = new DailyChallengeTimeRemainingRing(room.Value) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -59,29 +59,29 @@ namespace osu.Game.Tests.Visual.DailyChallenge AddStep("just started", () => { - room.Value.StartDate.Value = DateTimeOffset.Now.AddMinutes(-1); - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now.AddMinutes(-1); + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); AddStep("midway through", () => { - room.Value.StartDate.Value = DateTimeOffset.Now.AddHours(-12); - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now.AddHours(-12); + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); AddStep("nearing end", () => { - room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-1).AddMinutes(8); - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now.AddDays(-1).AddMinutes(8); + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); AddStep("already ended", () => { - room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-2); - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now.AddDays(-2); + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); AddSliderStep("manual progress", 0f, 1f, 0f, progress => { var startedTimeAgo = TimeSpan.FromHours(24) * progress; - room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo; - room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1); + room.Value.StartDate = DateTimeOffset.Now - startedTimeAgo; + room.Value.EndDate = room.Value.StartDate.Value.AddDays(1); }); } } diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 9b00449e11..bc6c3ed709 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -48,8 +48,8 @@ namespace osu.Game.Tests.Visual.Menus { new PlaylistItem(beatmap) }, - StartDate = { Value = DateTimeOffset.Now.AddMinutes(-30) }, - EndDate = { Value = DateTimeOffset.Now.AddSeconds(60) } + StartDate = DateTimeOffset.Now.AddMinutes(-30), + EndDate = DateTimeOffset.Now.AddSeconds(60) }); return true; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index b5cfecfa25..31923b676a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Multiplayer room", Status = new RoomStatusOpen(), - EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + EndDate = DateTimeOffset.Now.AddDays(1), Type = MatchType.HeadToHead, Playlist = { item1 }, CurrentPlaylistItem = item1 @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Private room", Status = new RoomStatusOpenPrivate(), Password = "*", - EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + EndDate = DateTimeOffset.Now.AddDays(1), Type = MatchType.HeadToHead, Playlist = { item3 }, CurrentPlaylistItem = item3 @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Playlist room with multiple beatmaps", Status = new RoomStatusPlaying(), - EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, + EndDate = DateTimeOffset.Now.AddDays(1), Playlist = { item1, item2 }, CurrentPlaylistItem = item1 }), @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Finished room", Status = new RoomStatusEnded(), - EndDate = { Value = DateTimeOffset.Now }, + EndDate = DateTimeOffset.Now, }), createLoungeRoom(new Room { diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 3652710e32..b45d1e68cb 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("create room", () => settings.ApplyButton.Action.Invoke()); AddAssert("has correct name", () => createdRoom.Name == expected_name); - AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration); + AddAssert("has correct duration", () => createdRoom.Duration == expectedDuration); } [Test] diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 7cc86e71bf..d0cbee9e50 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Playlists room.Name = "my awesome room"; room.Host = API.LocalUser.Value; room.RecentParticipants.Add(room.Host); - room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); + room.EndDate = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID @@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Playlists room.MaxAttempts.Value = 5; room.Host = API.LocalUser.Value; room.RecentParticipants.Add(room.Host); - room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); + room.EndDate = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs index 3894b6ea5b..8b17749afc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs @@ -58,8 +58,8 @@ namespace osu.Game.Tests.Visual.UserInterface { 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; @@ -136,8 +136,8 @@ namespace osu.Game.Tests.Visual.UserInterface { 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/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 6a7d460922..fe0b4c6a21 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -206,7 +206,7 @@ namespace osu.Game.Online.Multiplayer 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); diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 0c8df642fa..cd797a9668 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -44,7 +44,7 @@ namespace osu.Game.Online.Rooms // API doesn't populate status so let's do it here. foreach (var room in Response) { - if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value) + if (room.EndDate != null && DateTimeOffset.Now >= room.EndDate) room.Status = new RoomStatusEnded(); else if (room.HasPassword) room.Status = new RoomStatusOpenPrivate(); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 61f944a728..fd958d8725 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -86,6 +86,39 @@ namespace osu.Game.Online.Rooms 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. /// @@ -189,6 +222,15 @@ namespace osu.Game.Online.Rooms [JsonConverter(typeof(SnakeCaseStringEnumConverter))] private RoomCategory category; + [JsonProperty("duration")] + private int? duration; + + [JsonProperty("starts_at")] + private DateTimeOffset? startDate; + + [JsonProperty("ends_at")] + private DateTimeOffset? endDate; + // Not yet serialised (not implemented). private int? maxParticipants; @@ -245,36 +287,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); - #region Properties only used for room creation request - - [Cached] - public readonly Bindable Duration = new Bindable(); - - [JsonProperty("duration")] - private int? duration - { - get => (int?)Duration.Value?.TotalMinutes; - set - { - if (value == null) - Duration.Value = null; - else - Duration.Value = TimeSpan.FromMinutes(value.Value); - } - } - - #endregion - - // Only supports retrieval for now - [Cached] - [JsonProperty("starts_at")] - public readonly Bindable StartDate = new Bindable(); - - // Only supports retrieval for now - [Cached] - [JsonProperty("ends_at")] - public readonly Bindable EndDate = new Bindable(); - // 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 @@ -307,7 +319,7 @@ namespace osu.Game.Online.Rooms Type = other.Type; MaxParticipants = other.MaxParticipants; ParticipantCount = other.ParticipantCount; - EndDate.Value = other.EndDate.Value; + EndDate = other.EndDate; UserScore.Value = other.UserScore.Value; QueueMode = other.QueueMode; AutoStartDuration = other.AutoStartDuration; diff --git a/osu.Game/Screens/Menu/DailyChallengeButton.cs b/osu.Game/Screens/Menu/DailyChallengeButton.cs index 0013c9e033..92db4d2d07 100644 --- a/osu.Game/Screens/Menu/DailyChallengeButton.cs +++ b/osu.Game/Screens/Menu/DailyChallengeButton.cs @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Menu Room = room; cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet; - if (room.StartDate.Value != null && room.RoomID != lastDailyChallengeRoomID) + if (room.StartDate != null && room.RoomID != lastDailyChallengeRoomID) { lastDailyChallengeRoomID = room.RoomID; @@ -163,7 +163,7 @@ namespace osu.Game.Screens.Menu 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)); } @@ -179,7 +179,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/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 968345c713..9559826dab 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -228,7 +228,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 +301,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, diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs index e86f26ad6b..bffbb664a3 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,6 +11,7 @@ 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; @@ -17,8 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { public partial class DailyChallengeTimeRemainingRing : OnlinePlayComposite { - private CircularProgress progress = null!; - private OsuSpriteText timeText = null!; + private readonly Room room; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -26,6 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge [Resolved] private OsuColour colours { get; set; } = null!; + private CircularProgress progress = null!; + private OsuSpriteText timeText = null!; + + public DailyChallengeTimeRemainingRing(Room room) + { + this.room = room; + } + [BackgroundDependencyLoader] private void load() { @@ -90,12 +99,23 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { base.LoadComplete(); - StartDate.BindValueChanged(_ => Scheduler.AddOnce(updateState)); - EndDate.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + room.PropertyChanged += onRoomPropertyChanged; updateState(); + FinishTransforms(true); } + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(Room.StartDate): + case nameof(Room.EndDate): + Scheduler.AddOnce(updateState); + break; + } + } + private ScheduledDelegate? scheduledUpdate; private void updateState() @@ -105,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge const float transition_duration = 300; - if (StartDate.Value == null || EndDate.Value == null || EndDate.Value < DateTimeOffset.Now) + if (room.StartDate == null || room.EndDate == null || room.EndDate < DateTimeOffset.Now) { timeText.Text = TimeSpan.Zero.ToString(@"hh\:mm\:ss"); progress.Progress = 0; @@ -114,8 +134,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge return; } - var roomDuration = EndDate.Value.Value - StartDate.Value.Value; - var remaining = EndDate.Value.Value - DateTimeOffset.Now; + var roomDuration = room.EndDate.Value - room.StartDate.Value; + var remaining = room.EndDate.Value - DateTimeOffset.Now; timeText.Text = remaining.ToString(@"hh\:mm\:ss"); progress.Progress = remaining.TotalSeconds / roomDuration.TotalSeconds; @@ -138,5 +158,11 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge scheduledUpdate = Scheduler.AddDelayed(updateState, 1000); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 390ea06c1b..7a22bf1a0b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -169,7 +169,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft }, - endDateInfo = new EndDateInfo + endDateInfo = new EndDateInfo(Room) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs index 844991095e..18c7972c06 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs @@ -2,49 +2,68 @@ // 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.Game.Graphics; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public partial class EndDateInfo : OnlinePlayComposite { - 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 +79,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/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 10d3695d14..b3dc617fd6 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -34,7 +34,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components TextFlow.Colour = Colour4.Black; Pill.Background.Alpha = 1; - EndDate.BindValueChanged(_ => updateDisplay()); room.PropertyChanged += onRoomPropertyChanged; updateDisplay(); @@ -43,8 +42,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.Status)) - updateDisplay(); + switch (e.PropertyName) + { + case nameof(Room.Status): + case nameof(Room.EndDate): + updateDisplay(); + break; + } } private void updateDisplay() diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index a2eba3188d..90288a1067 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -341,8 +341,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge r.RoomID = null; // Null out dates because end date is not supported client-side and the settings overlay will populate a duration. - r.EndDate.Value = null; - r.Duration.Value = null; + r.EndDate = null; + r.Duration = null; Open(r); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 70418d1a7e..66196d1df6 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; @@ -32,14 +31,5 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] public Bindable UserScore { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable StartDate { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable EndDate { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected Bindable Duration { get; private set; } = null!; } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index 4b00678b01..5f1037bff1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.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.Allocation; @@ -17,20 +15,20 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsReadyButton : ReadyButton { - [Resolved(typeof(Room), nameof(Room.EndDate))] - private Bindable endDate { get; set; } - [Resolved(typeof(Room), nameof(Room.MaxAttempts))] - private Bindable maxAttempts { get; set; } + private Bindable maxAttempts { get; set; } = null!; [Resolved(typeof(Room), nameof(Room.UserScore))] - private Bindable userScore { get; set; } + private Bindable userScore { get; set; } = null!; [Resolved] - private IBindable gameBeatmap { get; set; } + private IBindable gameBeatmap { get; set; } = null!; - public PlaylistsReadyButton() + private readonly Room room; + + public PlaylistsReadyButton(Room room) { + this.room = room; Text = "Start"; } @@ -80,6 +78,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private bool enoughTimeLeft => // This should probably consider the length of the currently selected item, rather than a constant 30 seconds. - endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value; + room.EndDate != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate; } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs index 5161de5f64..0d837423a6 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs @@ -1,26 +1,25 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.Rooms; using osuTK; namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsRoomFooter : CompositeDrawable { - public Action OnStart; + public Action? OnStart; - public PlaylistsRoomFooter() + public PlaylistsRoomFooter(Room room) { RelativeSizeAxes = Axes.Both; InternalChildren = new[] { - new PlaylistsReadyButton + new PlaylistsReadyButton(room) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 20fbe42dd5..63bfa8f45d 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -317,7 +317,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }; MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); - Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true); DurationField.Current.BindValueChanged(duration => { @@ -346,6 +345,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists updateRoomName(); updateRoomAvailability(); updateRoomMaxParticipants(); + updateRoomDuration(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -363,6 +363,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.MaxParticipants): updateRoomMaxParticipants(); break; + + case nameof(Room.Duration): + updateRoomDuration(); + break; } } @@ -375,6 +379,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomMaxParticipants() => MaxParticipantsField.Text = room.MaxParticipants?.ToString(); + private void updateRoomDuration() + => DurationField.Current.Value = room.Duration ?? TimeSpan.FromMinutes(30); + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -431,7 +438,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists else MaxAttempts.Value = null; - Duration.Value = DurationField.Current.Value; + room.Duration = DurationField.Current.Value; loadingLayer.Show(); manager?.CreateRoom(room, onSuccess, onError); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 9780c48f1f..6d9bf29687 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -234,7 +234,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists } }; - protected override Drawable CreateFooter() => new PlaylistsRoomFooter + protected override Drawable CreateFooter() => new PlaylistsRoomFooter(Room) { OnStart = StartPlay }; diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index 3db9e12ab7..de54ff57e8 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay RoomID = -currentRoomId, Name = $@"Room {currentRoomId}", Host = new APIUser { Username = @"Host" }, - EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) }, + Duration = TimeSpan.FromSeconds(10), Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal, }; From 6c84e425f8c71ac1a850c8d2e43e6be6cc1c2d81 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 20:32:34 +0900 Subject: [PATCH 27/73] Make `Room.MaxAttempts` non-bindable --- .../TestScenePlaylistsRoomCreation.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 23 ++++++------ .../Components/RoomLocalUserInfo.cs | 36 +++++++++++++------ .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Playlists/PlaylistsReadyButton.cs | 7 ++-- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 19 +++++----- .../Playlists/PlaylistsRoomSubScreen.cs | 19 +++++++--- 7 files changed, 66 insertions(+), 43 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index d0cbee9e50..031f9c272f 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Playlists setupAndCreateRoom(room => { room.Name = "my awesome room"; - room.MaxAttempts.Value = 5; + room.MaxAttempts = 5; room.Host = API.LocalUser.Value; room.RecentParticipants.Add(room.Host); room.EndDate = DateTimeOffset.Now.AddMinutes(5); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index fd958d8725..cfb3b30ddb 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -146,6 +146,15 @@ namespace osu.Game.Online.Rooms 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 playlist queueing mode. Only valid for multiplayer rooms. /// @@ -237,6 +246,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("participant_count")] private int participantCount; + [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] + private int? maxAttempts; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] [JsonProperty("type")] private MatchType type; @@ -276,9 +288,6 @@ namespace osu.Game.Online.Rooms [Cached] public readonly Bindable DifficultyRange = new Bindable(); - [Cached] - public readonly Bindable MaxAttempts = new Bindable(); - [Cached] [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); @@ -287,14 +296,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); - // 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; - } - /// /// Copies values from another into this one. /// diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs index 0c3b53266c..974c296399 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 { - 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,27 @@ namespace osu.Game.Screens.OnlinePlay.Components { base.LoadComplete(); - MaxAttempts.BindValueChanged(_ => updateAttempts()); - UserScore.BindValueChanged(_ => updateAttempts(), true); + UserScore.BindValueChanged(_ => updateAttempts()); + + room.PropertyChanged += onRoomPropertyChanged; + updateAttempts(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.MaxAttempts)) + updateAttempts(); } 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) { - int remaining = MaxAttempts.Value.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts); + int remaining = room.MaxAttempts.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts); attemptDisplay.Text += $" ({remaining} remaining)"; if (remaining == 0) @@ -69,5 +79,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/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 66196d1df6..1880fdc974 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -26,9 +26,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable MaxAttempts { get; private set; } = null!; - [Resolved(typeof(Room))] public Bindable UserScore { get; private set; } = null!; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index 5f1037bff1..ca661b041a 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs @@ -15,9 +15,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsReadyButton : ReadyButton { - [Resolved(typeof(Room), nameof(Room.MaxAttempts))] - private Bindable maxAttempts { get; set; } = null!; - [Resolved(typeof(Room), nameof(Room.UserScore))] private Bindable userScore { get; set; } = null!; @@ -46,10 +43,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists userScore.BindValueChanged(aggregate => { - if (maxAttempts.Value == null) + if (room.MaxAttempts == null) return; - int remaining = maxAttempts.Value.Value - aggregate.NewValue.PlaylistItemAttempts.Sum(a => a.Attempts); + int remaining = room.MaxAttempts.Value - aggregate.NewValue.PlaylistItemAttempts.Sum(a => a.Attempts); hasRemainingAttempts = remaining > 0; }); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 63bfa8f45d..6550b4c2f1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -316,8 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists loadingLayer = new LoadingLayer(true) }; - MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true); - DurationField.Current.BindValueChanged(duration => { if (hasValidDuration) @@ -346,6 +344,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists updateRoomAvailability(); updateRoomMaxParticipants(); updateRoomDuration(); + updateRoomMaxAttempts(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -367,6 +366,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.Duration): updateRoomDuration(); break; + + case nameof(Room.MaxAttempts): + updateRoomMaxAttempts(); + break; } } @@ -382,6 +385,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomDuration() => DurationField.Current.Value = room.Duration ?? TimeSpan.FromMinutes(30); + private void updateRoomMaxAttempts() + => MaxAttemptsField.Text = room.MaxAttempts?.ToString(); + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -431,13 +437,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists room.Name = NameField.Text; room.Availability = AvailabilityPicker.Current.Value; - room.MaxParticipants = int.TryParse(MaxParticipantsField.Text, out int max) ? max : null; - - if (int.TryParse(MaxAttemptsField.Text, out max)) - MaxAttempts.Value = max; - else - MaxAttempts.Value = null; - + room.MaxParticipants = int.TryParse(MaxParticipantsField.Text, out int maxParticipants) ? maxParticipants : null; + room.MaxAttempts = int.TryParse(MaxAttemptsField.Text, out int maxAttempts) ? maxAttempts : null; room.Duration = DurationField.Current.Value; loadingLayer.Show(); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 6d9bf29687..079e3b03b7 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -60,16 +60,24 @@ namespace osu.Game.Screens.OnlinePlay.Playlists base.LoadComplete(); isIdle.BindValueChanged(_ => updatePollingRate(), true); - Room.MaxAttempts.BindValueChanged(_ => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true); Room.PropertyChanged += onRoomPropertyChanged; updateSetupState(); + updateRoomMaxAttempts(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.RoomID)) - updateSetupState(); + switch (e.PropertyName) + { + case nameof(Room.RoomID): + updateSetupState(); + break; + + case nameof(Room.MaxAttempts): + updateRoomMaxAttempts(); + break; + } } private void updateSetupState() @@ -82,6 +90,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists } } + private void updateRoomMaxAttempts() + => progressSection.Alpha = Room.MaxAttempts != null ? 1 : 0; + protected override Drawable CreateMainContent() => new Container { RelativeSizeAxes = Axes.Both, @@ -193,7 +204,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Children = new Drawable[] { new OverlinedHeader("Progress"), - new RoomLocalUserInfo(), + new RoomLocalUserInfo(Room), } }, }, From 80b3e330a6daf7cfd95a5b1e69148f033826b8b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 22:23:33 +0900 Subject: [PATCH 28/73] Make `Room.ChannelId` non-bindable --- osu.Game/Online/Rooms/Room.cs | 18 +++++++++++----- .../Match/Components/MatchChatDisplay.cs | 21 ++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index cfb3b30ddb..ea3e8d73fe 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -194,6 +194,15 @@ namespace osu.Game.Online.Rooms 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. /// @@ -266,6 +275,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; + [JsonProperty("channel_id")] + private int channelId; + // Not serialised (see: GetRoomsRequest). private RoomStatus status = new RoomStatusOpen(); @@ -276,10 +288,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); - [Cached] - [JsonProperty("channel_id")] - public readonly Bindable ChannelId = new Bindable(); - [JsonProperty("playlist_item_stats")] [Cached] public readonly Bindable PlaylistItemStats = new Bindable(); @@ -313,7 +321,7 @@ namespace osu.Game.Online.Rooms if (other.Host != null && Host?.Id != other.Host.Id) Host = other.Host; - ChannelId.Value = other.ChannelId.Value; + ChannelId = other.ChannelId; Status = other.Status; Availability = other.Availability; HasPassword = other.HasPassword; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs index ed4f0ad6d8..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 == 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); } From 487a010b12eec172c1e5aa8dd2ad9f64a973508e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 22:29:45 +0900 Subject: [PATCH 29/73] Make `Room.PlaylistItemStats` non-bindable --- osu.Game/Online/Rooms/Room.cs | 18 ++++++++--- .../Lounge/Components/DrawableRoom.cs | 2 +- .../Lounge/Components/PlaylistCountPill.cs | 31 ++++++++++++++++--- .../Lounge/Components/RoomsContainer.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Visual/OnlinePlay/TestRoomManager.cs | 2 +- 6 files changed, 43 insertions(+), 15 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index ea3e8d73fe..dd3a9b2e73 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -155,6 +155,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref maxAttempts, value); } + /// + /// Describes the items in the playlist. + /// + public RoomPlaylistItemStats? PlaylistItemStats + { + get => playlistItemStats; + set => SetField(ref playlistItemStats, value); + } + /// /// The playlist queueing mode. Only valid for multiplayer rooms. /// @@ -258,6 +267,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] private int? maxAttempts; + [JsonProperty("playlist_item_stats")] + private RoomPlaylistItemStats? playlistItemStats; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] [JsonProperty("type")] private MatchType type; @@ -288,10 +300,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); - [JsonProperty("playlist_item_stats")] - [Cached] - public readonly Bindable PlaylistItemStats = new Bindable(); - [JsonProperty("difficulty_range")] [Cached] public readonly Bindable DifficultyRange = new Bindable(); @@ -333,7 +341,7 @@ namespace osu.Game.Online.Rooms QueueMode = other.QueueMode; AutoStartDuration = other.AutoStartDuration; DifficultyRange.Value = other.DifficultyRange.Value; - PlaylistItemStats.Value = other.PlaylistItemStats.Value; + PlaylistItemStats = other.PlaylistItemStats; CurrentPlaylistItem = other.CurrentPlaylistItem; AutoSkip = other.AutoSkip; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 7a22bf1a0b..ddfbe00227 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -355,7 +355,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components pills.AddRange(new Drawable[] { - new PlaylistCountPill + new PlaylistCountPill(Room) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index fe5ccb4f09..7e530993dd 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,47 @@ 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); + Playlist.BindCollectionChanged((_, _) => updateCount()); + + room.PropertyChanged += onRoomPropertyChanged; + updateCount(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.PlaylistItemStats)) + updateCount(); } private void updateCount() { - int activeItems = Playlist.Count > 0 || PlaylistItemStats.Value == null + int activeItems = 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.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/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 11ad886773..17aed021b2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -79,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)) { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 1880fdc974..46debb5b9d 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -14,9 +14,6 @@ namespace osu.Game.Screens.OnlinePlay /// public partial class OnlinePlayComposite : CompositeDrawable { - [Resolved(typeof(Room))] - protected Bindable PlaylistItemStats { get; private set; } = null!; - [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } = null!; diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index de54ff57e8..a384a14441 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay if (ruleset != null) { - room.PlaylistItemStats.Value = new Room.RoomPlaylistItemStats + room.PlaylistItemStats = new Room.RoomPlaylistItemStats { RulesetIDs = new[] { ruleset.OnlineID }, }; From c4f8fd183216d537f3d2ee47d4c54c561cee3f3c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 22:33:49 +0900 Subject: [PATCH 30/73] Make `Room.DifficultyRange` non-bindable --- .../TestSceneStarRatingRangeDisplay.cs | 2 +- osu.Game/Online/Rooms/Room.cs | 18 +++++--- .../Components/StarRatingRangeDisplay.cs | 44 +++++++++++++------ .../Lounge/Components/DrawableRoom.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs index b53a61f881..5c11690cd6 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 diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index dd3a9b2e73..9d69e791c7 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -164,6 +164,15 @@ namespace osu.Game.Online.Rooms 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. /// @@ -270,6 +279,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist_item_stats")] private RoomPlaylistItemStats? playlistItemStats; + [JsonProperty("difficulty_range")] + private RoomDifficultyRange? difficultyRange; + [JsonConverter(typeof(SnakeCaseStringEnumConverter))] [JsonProperty("type")] private MatchType type; @@ -300,10 +312,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); - [JsonProperty("difficulty_range")] - [Cached] - public readonly Bindable DifficultyRange = new Bindable(); - [Cached] [JsonProperty("current_user_score")] public readonly Bindable UserScore = new Bindable(); @@ -340,7 +348,7 @@ namespace osu.Game.Online.Rooms UserScore.Value = other.UserScore.Value; QueueMode = other.QueueMode; AutoStartDuration = other.AutoStartDuration; - DifficultyRange.Value = other.DifficultyRange.Value; + DifficultyRange = other.DifficultyRange; PlaylistItemStats = other.PlaylistItemStats; CurrentPlaylistItem = other.CurrentPlaylistItem; AutoSkip = other.AutoSkip; diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 2ee3bb30dd..13e3dc6d7c 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 { + 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,16 @@ namespace osu.Game.Screens.OnlinePlay.Components { base.LoadComplete(); - DifficultyRange.BindValueChanged(_ => updateRange()); - Playlist.BindCollectionChanged((_, _) => updateRange(), true); + Playlist.BindCollectionChanged((_, _) => updateRange()); + + room.PropertyChanged += onRoomPropertyChanged; + updateRange(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.DifficultyRange)) + updateRange(); } private void updateRange() @@ -85,11 +97,11 @@ namespace osu.Game.Screens.OnlinePlay.Components StarDifficulty minDifficulty; StarDifficulty maxDifficulty; - if (DifficultyRange.Value != null && Playlist.Count == 0) + if (room.DifficultyRange != null && 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 { @@ -107,5 +119,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/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index ddfbe00227..c368666ac2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -360,7 +360,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - new StarRatingRangeDisplay + new StarRatingRangeDisplay(Room) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 46debb5b9d..66e43c50b7 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -17,9 +17,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } = null!; - [Resolved(typeof(Room))] - protected Bindable DifficultyRange { get; private set; } = null!; - [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; From dc5337d771ef035ae66911a0de85863661103d65 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Nov 2024 22:39:01 +0900 Subject: [PATCH 31/73] Make `Room.UserScore` non-bindable --- osu.Game/Online/Rooms/Room.cs | 18 +++++++--- .../Components/RoomLocalUserInfo.cs | 15 +++++---- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 3 -- .../Playlists/PlaylistsReadyButton.cs | 33 +++++++++++++------ 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 9d69e791c7..8fb3901a79 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -200,6 +200,15 @@ namespace osu.Game.Online.Rooms 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. /// @@ -296,6 +305,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("auto_start_duration")] private ushort autoStartDuration; + [JsonProperty("current_user_score")] + private PlaylistAggregateScore? userScore; + [JsonProperty("current_playlist_item")] private PlaylistItem? currentPlaylistItem; @@ -312,10 +324,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); - [Cached] - [JsonProperty("current_user_score")] - public readonly Bindable UserScore = new Bindable(); - [Cached] [JsonProperty("recent_participants")] public readonly BindableList RecentParticipants = new BindableList(); @@ -345,7 +353,7 @@ namespace osu.Game.Online.Rooms MaxParticipants = other.MaxParticipants; ParticipantCount = other.ParticipantCount; EndDate = other.EndDate; - UserScore.Value = other.UserScore.Value; + UserScore = other.UserScore; QueueMode = other.QueueMode; AutoStartDuration = other.AutoStartDuration; DifficultyRange = other.DifficultyRange; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs index 974c296399..b3e60d4318 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs @@ -47,16 +47,19 @@ namespace osu.Game.Screens.OnlinePlay.Components { base.LoadComplete(); - UserScore.BindValueChanged(_ => updateAttempts()); - room.PropertyChanged += onRoomPropertyChanged; updateAttempts(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.MaxAttempts)) - updateAttempts(); + switch (e.PropertyName) + { + case nameof(Room.UserScore): + case nameof(Room.MaxAttempts): + updateAttempts(); + break; + } } private void updateAttempts() @@ -65,9 +68,9 @@ namespace osu.Game.Screens.OnlinePlay.Components { attemptDisplay.Text = $"Maximum attempts: {room.MaxAttempts:N0}"; - if (UserScore.Value != null) + if (room.UserScore != null) { - int remaining = room.MaxAttempts.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) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 66e43c50b7..82fc6d535c 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -19,8 +19,5 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(typeof(Room))] protected BindableList RecentParticipants { get; private set; } = null!; - - [Resolved(typeof(Room))] - public Bindable UserScore { get; private set; } = null!; } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index ca661b041a..a460779ea6 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -15,9 +16,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsReadyButton : ReadyButton { - [Resolved(typeof(Room), nameof(Room.UserScore))] - private Bindable userScore { get; set; } = null!; - [Resolved] private IBindable gameBeatmap { get; set; } = null!; @@ -41,15 +39,24 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { base.LoadComplete(); - userScore.BindValueChanged(aggregate => - { - if (room.MaxAttempts == null) - return; + room.PropertyChanged += onRoomPropertyChanged; + updateRoomUserScore(); + } - int remaining = room.MaxAttempts.Value - aggregate.NewValue.PlaylistItemAttempts.Sum(a => a.Attempts); + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.UserScore)) + updateRoomUserScore(); + } - hasRemainingAttempts = remaining > 0; - }); + private void updateRoomUserScore() + { + if (room.MaxAttempts == null || room.UserScore == null) + return; + + int remaining = room.MaxAttempts.Value - room.UserScore.PlaylistItemAttempts.Sum(a => a.Attempts); + + hasRemainingAttempts = remaining > 0; } protected override void Update() @@ -76,5 +83,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private bool enoughTimeLeft => // This should probably consider the length of the currently selected item, rather than a constant 30 seconds. room.EndDate != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } } } From b16edbbf524b8f95cb9a67f2bb17475dc2e34bad Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Nov 2024 15:07:16 +0900 Subject: [PATCH 32/73] Make `Room.RecentParticipants` non-bindable --- .../Multiplayer/TestSceneDrawableRoom.cs | 4 +- .../TestSceneDrawableRoomParticipantsList.cs | 6 +- .../TestScenePlaylistsParticipantsList.cs | 12 ++-- .../TestScenePlaylistsRoomCreation.cs | 4 +- .../Online/Multiplayer/MultiplayerClient.cs | 6 +- osu.Game/Online/Rooms/Room.cs | 32 ++++++--- .../Components/ParticipantsDisplay.cs | 2 +- .../OnlinePlay/Components/ParticipantsList.cs | 41 ++++++++---- .../DrawableRoomParticipantsList.cs | 66 ++++++++----------- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 4 -- .../OnlinePlay/TestRoomRequestsHandler.cs | 2 +- 11 files changed, 100 insertions(+), 79 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 31923b676a..d00ff72599 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -199,11 +199,11 @@ namespace osu.Game.Tests.Visual.Multiplayer if (room.RecentParticipants.Count == 0) { - room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new APIUser + room.RecentParticipants = Enumerable.Range(0, 20).Select(i => new APIUser { Id = i, Username = $"User {i}" - })); + }).ToArray(); } return new DrawableLoungeRoom(room) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index 3d0c1be22c..b909b934b3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -138,17 +138,17 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(int id) { - SelectedRoom.Value.RecentParticipants.Add(new APIUser + SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Append(new APIUser { Id = id, Username = $"User {id}" - }); + }).ToArray(); SelectedRoom.Value.ParticipantCount++; } private void removeUserAt(int index) { - SelectedRoom.Value.RecentParticipants.RemoveAt(index); + SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value.RecentParticipants[index])).ToArray(); SelectedRoom.Value.ParticipantCount--; } } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index 2368a2068d..f72a2cd655 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; @@ -19,17 +20,16 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("create list", () => { - SelectedRoom.Value = new Room { RoomID = 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() + }; }); } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 031f9c272f..f75b3d7b14 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name = "my awesome room"; room.Host = API.LocalUser.Value; - room.RecentParticipants.Add(room.Host); + room.RecentParticipants = [room.Host]; room.EndDate = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Playlists room.Name = "my awesome room"; room.MaxAttempts = 5; room.Host = API.LocalUser.Value; - room.RecentParticipants.Add(room.Host); + room.RecentParticipants = [room.Host]; room.EndDate = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index fe0b4c6a21..74f89c7a61 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -487,11 +487,11 @@ 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]" - }); + }).ToArray(); APIRoom.ParticipantCount++; } @@ -506,7 +506,7 @@ namespace osu.Game.Online.Multiplayer PlayingUserIds.Remove(user.UserID); Debug.Assert(APIRoom != null); - APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID); + APIRoom.RecentParticipants = APIRoom.RecentParticipants.Where(u => u.Id != user.UserID).ToArray(); APIRoom.ParticipantCount--; callback?.Invoke(user); diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index 8fb3901a79..f3f8e87e54 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -137,6 +137,15 @@ namespace osu.Game.Online.Rooms 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. /// @@ -282,6 +291,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("participant_count")] private int participantCount; + [JsonProperty("recent_participants")] + private IReadOnlyList recentParticipants = []; + [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] private int? maxAttempts; @@ -324,10 +336,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("playlist")] public readonly BindableList Playlist = new BindableList(); - [Cached] - [JsonProperty("recent_participants")] - public readonly BindableList RecentParticipants = new BindableList(); - /// /// Copies values from another into this one. /// @@ -369,11 +377,7 @@ namespace osu.Game.Online.Rooms Playlist.AddRange(other.Playlist); } - if (!RecentParticipants.SequenceEqual(other.RecentParticipants)) - { - RecentParticipants.Clear(); - RecentParticipants.AddRange(other.RecentParticipants); - } + RecentParticipants = other.RecentParticipants; } public void RemoveExpiredPlaylistItems() @@ -411,6 +415,16 @@ namespace osu.Game.Online.Rooms 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)) diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs index 1ae2756514..5a040dcadb 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Components AddInternal(scroll = new OsuScrollContainer(direction) { - Child = list = new ParticipantsList() + Child = list = new ParticipantsList(room) }); switch (direction) diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index c4aefe4f99..1be23014bd 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -1,15 +1,14 @@ // 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; @@ -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/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index 06ae939842..d989bd6c82 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.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.Collections.Specialized; +using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -165,12 +164,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - RecentParticipants.BindCollectionChanged(onParticipantsChanged, true); - room.PropertyChanged += onRoomPropertyChanged; updateRoomHost(); updateRoomParticipantCount(); + updateRoomParticipants(); } private int numberOfCircles = 4; @@ -190,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(); } @@ -239,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(); @@ -253,7 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateHiddenUsers() { int hiddenCount = 0; - if (RecentParticipants.Count > NumberOfCircles) + if (room.RecentParticipants.Count > NumberOfCircles) hiddenCount = room.ParticipantCount - NumberOfCircles + 1; hiddenUsers.Count = hiddenCount; @@ -262,7 +250,7 @@ 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); } } @@ -278,6 +266,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components case nameof(Room.ParticipantCount): updateRoomParticipantCount(); break; + + case nameof(Room.RecentParticipants): + updateRoomParticipants(); + break; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 82fc6d535c..ffe36d7609 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay @@ -16,8 +15,5 @@ namespace osu.Game.Screens.OnlinePlay { [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } = null!; - - [Resolved(typeof(Room))] - protected BindableList RecentParticipants { get; private set; } = null!; } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index 0934c4f3dc..37325c2d6b 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -280,7 +280,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay responseRoom.Password = responseRoom.HasPassword ? Guid.NewGuid().ToString() : null; if (!withParticipants) - responseRoom.RecentParticipants.Clear(); + responseRoom.RecentParticipants = []; return responseRoom; } From 34c0f72dd6801da1a45a4e3627a1888f1687cde1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Nov 2024 17:57:32 +0900 Subject: [PATCH 33/73] Make `Room.Playlist` non-bindable --- .../DailyChallenge/TestSceneDailyChallenge.cs | 12 +- .../TestSceneDailyChallengeIntro.cs | 4 +- .../Visual/Menus/TestSceneMainMenu.cs | 4 +- .../Visual/Multiplayer/QueueModeTestScene.cs | 4 +- .../Multiplayer/TestSceneDrawableRoom.cs | 6 +- .../TestSceneMatchBeatmapDetailArea.cs | 7 +- .../Multiplayer/TestSceneMatchStartControl.cs | 2 +- .../Multiplayer/TestSceneMultiplayer.cs | 110 ++++++++--------- .../TestSceneMultiplayerMatchSubScreen.cs | 114 +++++++++++------- .../TestSceneMultiplayerPlaylist.cs | 6 +- .../TestSceneMultiplayerQueueList.cs | 19 +-- .../TestScenePlaylistsSongSelect.cs | 7 +- .../TestSceneStarRatingRangeDisplay.cs | 6 +- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 16 +-- .../TestScenePlaylistsMatchSettingsOverlay.cs | 10 +- .../TestScenePlaylistsRoomCreation.cs | 58 +++++---- .../UserInterface/TestSceneMainMenuButton.cs | 8 +- .../Online/Multiplayer/MultiplayerClient.cs | 34 +----- osu.Game/Online/Rooms/PlaylistExtensions.cs | 5 +- osu.Game/Online/Rooms/PlaylistItem.cs | 25 ++-- osu.Game/Online/Rooms/Room.cs | 26 ++-- .../OnlinePlay/Components/BeatmapTitle.cs | 34 ++++-- .../Components/MatchBeatmapDetailArea.cs | 39 ++++-- .../Components/OverlinedPlaylistHeader.cs | 24 +++- .../Components/StarRatingRangeDisplay.cs | 15 ++- .../Lounge/Components/PlaylistCountPill.cs | 15 ++- .../Lounge/LoungeBackgroundScreen.cs | 32 ++++- .../Match/MultiplayerMatchSettingsOverlay.cs | 19 ++- .../Match/Playlist/MultiplayerPlaylist.cs | 8 +- .../Match/Playlist/MultiplayerQueueList.cs | 52 +++++--- .../Multiplayer/MultiplayerMatchSongSelect.cs | 5 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Screens/OnlinePlay/OnlinePlayComposite.cs | 8 +- .../OnlinePlay/OnlinePlaySongSelect.cs | 3 - .../Playlists/PlaylistsRoomSettingsOverlay.cs | 19 ++- .../Playlists/PlaylistsRoomSubScreen.cs | 13 +- .../Playlists/PlaylistsSongSelect.cs | 37 ++---- .../Multiplayer/MultiplayerTestScene.cs | 4 +- .../Multiplayer/TestMultiplayerClient.cs | 6 +- .../Visual/OnlinePlay/TestRoomManager.cs | 11 +- .../OnlinePlay/TestRoomRequestsHandler.cs | 22 ++-- 41 files changed, 490 insertions(+), 361 deletions(-) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index 1a19c423fc..0742ed5eb9 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -42,13 +42,13 @@ namespace osu.Game.Tests.Visual.DailyChallenge RoomID = 1234, Name = "Daily Challenge: June 4, 2024", Playlist = - { + [ new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) { RequiredMods = [new APIMod(new OsuModTraceable())], AllowedMods = [new APIMod(new OsuModDoubleTime())] } - }, + ], EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; @@ -65,13 +65,13 @@ namespace osu.Game.Tests.Visual.DailyChallenge RoomID = 1234, Name = "Daily Challenge: June 4, 2024", Playlist = - { + [ new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) { RequiredMods = [new APIMod(new OsuModTraceable())], AllowedMods = [new APIMod(new OsuModDoubleTime())] } - }, + ], EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; @@ -94,13 +94,13 @@ namespace osu.Game.Tests.Visual.DailyChallenge RoomID = 1234, Name = "Daily Challenge: June 4, 2024", Playlist = - { + [ new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First()) { RequiredMods = [new APIMod(new OsuModTraceable())], AllowedMods = [new APIMod(new OsuModDoubleTime())] } - }, + ], EndDate = DateTimeOffset.Now.AddHours(12), Category = RoomCategory.DailyChallenge }; diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs index aa311766d5..b0d600c7d5 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeIntro.cs @@ -71,13 +71,13 @@ namespace osu.Game.Tests.Visual.DailyChallenge RoomID = roomId, Name = "Daily Challenge: June 4, 2024", Playlist = - { + [ new PlaylistItem(CreateAPIBeatmap(new OsuRuleset().RulesetInfo)) { RequiredMods = [new APIMod(new OsuModTraceable())], AllowedMods = [new APIMod(new OsuModDoubleTime())] } - }, + ], StartDate = DateTimeOffset.Now, EndDate = DateTimeOffset.Now.AddHours(24), Category = RoomCategory.DailyChallenge diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index bc6c3ed709..f3ea20c1aa 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -45,9 +45,9 @@ namespace osu.Game.Tests.Visual.Menus RoomID = 1234, Name = "Aug 8, 2024", Playlist = - { + [ new PlaylistItem(beatmap) - }, + ], StartDate = DateTimeOffset.Now.AddMinutes(-30), EndDate = DateTimeOffset.Now.AddSeconds(60) }); diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 7dce7daf6d..2b738743ea 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -76,12 +76,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = Mode, Playlist = - { + [ new PlaylistItem(InitialBeatmap) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] })); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index d00ff72599..e5938a796c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Status = new RoomStatusOpen(), EndDate = DateTimeOffset.Now.AddDays(1), Type = MatchType.HeadToHead, - Playlist = { item1 }, + Playlist = [item1], CurrentPlaylistItem = item1 }), createLoungeRoom(new Room @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Password = "*", EndDate = DateTimeOffset.Now.AddDays(1), Type = MatchType.HeadToHead, - Playlist = { item3 }, + Playlist = [item3], CurrentPlaylistItem = item3 }), createLoungeRoom(new Room @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Playlist room with multiple beatmaps", Status = new RoomStatusPlaying(), EndDate = DateTimeOffset.Now.AddDays(1), - Playlist = { item1, item2 }, + Playlist = [item1, item2], CurrentPlaylistItem = item1 }), createLoungeRoom(new Room diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index 24d1b51ff8..dd39db49c5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.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 osu.Framework.Graphics; using osu.Game.Online.API; using osu.Game.Online.Rooms; @@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { SelectedRoom.Value = new Room(); - Child = new MatchBeatmapDetailArea + Child = new MatchBeatmapDetailArea(SelectedRoom.Value) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -35,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createNewItem() { - SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) + SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { ID = SelectedRoom.Value.Playlist.Count, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, @@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer new APIMod(new OsuModDoubleTime()), new APIMod(new OsuModAutoplay()) } - }); + }).ToArray(); } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 4eddac8c7a..6bb5ca254b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Multiplayer room.Value = new Room { - Playlist = { item }, + Playlist = [item], CurrentPlaylistItem = item }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index e625d23779..9213a52c0e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -105,12 +105,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddRepeatStep("random stuff happens", performRandomAction, 30); @@ -240,12 +240,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1); @@ -261,12 +261,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }, API.LocalUser.Value); }); @@ -290,12 +290,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }, API.LocalUser.Value); }); @@ -320,12 +320,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", Password = "password", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password == "password"); @@ -341,12 +341,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", Password = "password", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }, API.LocalUser.Value); }); @@ -373,12 +373,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", Password = "password", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2")); @@ -403,12 +403,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); pressReadyButton(); @@ -431,7 +431,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new Room { Name = "Test Room", - Playlist = { item } + Playlist = [item] }; }); @@ -472,7 +472,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new Room { Name = "Test Room", - Playlist = { item } + Playlist = [item] }; }); @@ -513,7 +513,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return new Room { Name = "Test Room", - Playlist = { item } + Playlist = [item] }; }); @@ -550,12 +550,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddStep("join other user (ready, host)", () => @@ -583,12 +583,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); @@ -622,12 +622,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddStep("disconnect", () => multiplayerClient.Disconnect()); @@ -641,13 +641,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, AllowedMods = new[] { new APIMod(new OsuModHidden()) } } - } + ] }); AddStep("open mod overlay", () => this.ChildrenOfType().Single().TriggerClick()); @@ -681,12 +681,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); enterGameplay(); @@ -726,12 +726,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); enterGameplay(); @@ -756,12 +756,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); pressReadyButton(); @@ -794,12 +794,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }, API.LocalUser.Value); }); @@ -811,11 +811,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("change server-side settings", () => { roomManager.ServerSideRooms[0].Name = "New name"; - roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) - { - ID = 2, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID, - }); + roomManager.ServerSideRooms[0].Playlist = + [ + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + ID = 2, + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + } + ]; }); AddStep("join room", () => InputManager.Key(Key.Enter)); @@ -825,8 +828,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("local room has correct settings", () => { var localRoom = this.ChildrenOfType().Single().Room; - return localRoom.Name == roomManager.ServerSideRooms[0].Name - && localRoom.Playlist.SequenceEqual(roomManager.ServerSideRooms[0].Playlist); + return localRoom.Name == roomManager.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2; }); } @@ -839,12 +841,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); @@ -875,12 +877,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating)); @@ -914,12 +916,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); enterGameplay(); @@ -945,12 +947,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); enterGameplay(); @@ -978,12 +980,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - } + ] }); AddStep("join other user and make host", () => @@ -1025,7 +1027,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "Test Room", QueueMode = QueueMode.AllPlayers, Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, @@ -1036,7 +1038,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RulesetID = new TaikoRuleset().RulesetInfo.OnlineID, AllowedMods = new[] { new APIMod { Acronym = "HD" } }, }, - } + ] }); AddStep("select hidden", () => multiplayerClient.ChangeUserMods(new[] { new APIMod { Acronym = "HD" } })); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 0c2a657c5a..ef5ff25194 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -100,10 +100,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -117,11 +120,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -140,10 +146,13 @@ namespace osu.Game.Tests.Visual.Multiplayer 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); @@ -155,10 +164,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -184,11 +196,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -211,11 +226,14 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -233,10 +251,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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(); @@ -254,8 +275,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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 +285,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { RulesetID = new OsuRuleset().RulesetInfo.OnlineID } - }); + ]; }); ClickButtonWhenEnabled(); @@ -291,19 +312,22 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) - { - RulesetID = new OsuRuleset().RulesetInfo.OnlineID, - RequiredMods = new[] + SelectedRoom.Value.Playlist = + [ + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { - new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }), - new APIMod(new OsuModStrictTracking()), - }, - AllowedMods = new[] - { - new APIMod(new OsuModFlashlight()), + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + RequiredMods = new[] + { + new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }), + new APIMod(new OsuModStrictTracking()), + }, + AllowedMods = new[] + { + new APIMod(new OsuModFlashlight()), + } } - }); + ]; }); ClickButtonWhenEnabled(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index 92b44d9502..518ea2b511 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -48,7 +48,7 @@ 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, @@ -166,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "test name", Playlist = - { + [ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) { RulesetID = Ruleset.Value.OnlineID @@ -176,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 10625d57aa..50f55ebde0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using NUnit.Framework; @@ -27,10 +25,10 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayerQueueList : MultiplayerTestScene { - private MultiplayerQueueList playlist; - private BeatmapManager beatmaps; - private BeatmapSetInfo importedSet; - private BeatmapInfo importedBeatmap; + private MultiplayerQueueList playlist = null!; + private BeatmapManager beatmaps = null!; + private BeatmapSetInfo importedSet = null!; + private BeatmapInfo importedBeatmap = null!; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create playlist", () => { - Child = playlist = new MultiplayerQueueList + Child = playlist = new MultiplayerQueueList(SelectedRoom.Value) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(500, 300), - Items = { BindTarget = MultiplayerClient.ClientAPIRoom!.Playlist } + }; + + MultiplayerClient.ClientAPIRoom!.PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(Room.Playlist)) + playlist.Items.ReplaceRange(0, playlist.Items.Count, MultiplayerClient.ClientAPIRoom.Playlist); }; }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index cc78bed5de..fea6617d3f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -99,12 +99,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { 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("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); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs index 5c11690cd6..195b40bd13 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs @@ -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 b68089015e..05136ebee1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -65,12 +65,12 @@ namespace osu.Game.Tests.Visual.Multiplayer 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); @@ -85,12 +85,12 @@ namespace osu.Game.Tests.Visual.Multiplayer 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); @@ -122,12 +122,12 @@ namespace osu.Game.Tests.Visual.Multiplayer 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 == MatchType.HeadToHead); @@ -147,12 +147,12 @@ namespace osu.Game.Tests.Visual.Multiplayer { Name = "Test Room", Playlist = - { + [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } - } + ] }); AddUntilStep("room type is head to head", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.HeadToHead); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index b45d1e68cb..321f563140 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("clear name and beatmap", () => { SelectedRoom.Value.Name = ""; - SelectedRoom.Value.Playlist.Clear(); + SelectedRoom.Value.Playlist = []; }); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists 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 = ""); @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Playlists { 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 => { @@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Playlists var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo; SelectedRoom.Value.Name = "Test Room"; - SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap)); + SelectedRoom.Value.Playlist = [new PlaylistItem(beatmap)]; errorMessage = $"{not_found_prefix} {beatmap.OnlineID}"; @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("setup", () => { SelectedRoom.Value.Name = "Test Room"; - SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)); + SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]; RoomManager.CreateRequested = _ => failText; }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index f75b3d7b14..6a53019b76 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -64,10 +64,13 @@ namespace osu.Game.Tests.Visual.Playlists room.Host = API.LocalUser.Value; room.RecentParticipants = [room.Host]; room.EndDate = DateTimeOffset.Now.AddMinutes(5); - room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) - { - RulesetID = new OsuRuleset().RulesetInfo.OnlineID - }); + room.Playlist = + [ + new PlaylistItem(importedBeatmap.Beatmaps.First()) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID + } + ]; }); AddUntilStep("Progress details are hidden", () => match.ChildrenOfType().FirstOrDefault()?.Parent!.Alpha == 0); @@ -88,10 +91,13 @@ namespace osu.Game.Tests.Visual.Playlists room.Host = API.LocalUser.Value; room.RecentParticipants = [room.Host]; room.EndDate = DateTimeOffset.Now.AddMinutes(5); - room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) - { - RulesetID = new OsuRuleset().RulesetInfo.OnlineID - }); + room.Playlist = + [ + new PlaylistItem(importedBeatmap.Beatmaps.First()) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID + } + ]; }); AddUntilStep("Progress details are visible", () => match.ChildrenOfType().FirstOrDefault()?.Parent!.Alpha == 1); @@ -104,10 +110,13 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name = "my awesome room"; room.Host = API.LocalUser.Value; - room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) - { - RulesetID = new OsuRuleset().RulesetInfo.OnlineID - }); + room.Playlist = + [ + new PlaylistItem(importedBeatmap.Beatmaps.First()) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID + } + ]; }); AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]); @@ -152,19 +161,22 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name = "my awesome room"; room.Host = API.LocalUser.Value; - room.Playlist.Add(new PlaylistItem(new BeatmapInfo - { - MD5Hash = realHash, - OnlineID = realOnlineId, - Metadata = new BeatmapMetadata(), - BeatmapSet = new BeatmapSetInfo + 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); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs index 8b17749afc..923c197c22 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneMainMenuButton.cs @@ -55,9 +55,9 @@ namespace osu.Game.Tests.Visual.UserInterface { RoomID = 1234, Playlist = - { + [ new PlaylistItem(beatmap) - }, + ], StartDate = DateTimeOffset.Now.AddMinutes(-5), EndDate = DateTimeOffset.Now.AddSeconds(30) }); @@ -133,9 +133,9 @@ namespace osu.Game.Tests.Visual.UserInterface { RoomID = 1234, Playlist = - { + [ new PlaylistItem(beatmap) - }, + ], StartDate = DateTimeOffset.Now.AddMinutes(-50), EndDate = DateTimeOffset.Now.AddSeconds(30) }); diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 74f89c7a61..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; @@ -201,8 +200,7 @@ 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.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. @@ -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(); 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 f3f8e87e54..deb5808861 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Runtime.CompilerServices; using Newtonsoft.Json; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Game.IO.Serialization.Converters; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; @@ -164,6 +163,15 @@ namespace osu.Game.Online.Rooms set => SetField(ref maxAttempts, value); } + /// + /// The room playlist. + /// + public IReadOnlyList Playlist + { + get => playlist; + set => SetList(ref playlist, value); + } + /// /// Describes the items in the playlist. /// @@ -297,6 +305,9 @@ namespace osu.Game.Online.Rooms [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] private int? maxAttempts; + [JsonProperty("playlist")] + private IReadOnlyList playlist = []; + [JsonProperty("playlist_item_stats")] private RoomPlaylistItemStats? playlistItemStats; @@ -332,10 +343,6 @@ namespace osu.Game.Online.Rooms // Not yet serialised (not implemented). private RoomAvailability availability; - [Cached] - [JsonProperty("playlist")] - public readonly BindableList Playlist = new BindableList(); - /// /// Copies values from another into this one. /// @@ -371,12 +378,7 @@ namespace osu.Game.Online.Rooms other.RemoveExpiredPlaylistItems(); - if (!Playlist.SequenceEqual(other.Playlist)) - { - Playlist.Clear(); - Playlist.AddRange(other.Playlist); - } - + Playlist = other.Playlist; RecentParticipants = other.RecentParticipants; } @@ -386,7 +388,7 @@ namespace osu.Game.Online.Rooms // 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 is not RoomStatusEnded) - Playlist.RemoveAll(i => i.Expired); + Playlist = Playlist.Where(i => !i.Expired).ToArray(); } [JsonObject(MemberSerialization.OptIn)] diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index 7c57f5b4f5..01b71539fe 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -8,25 +9,31 @@ 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 { + 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 +53,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 +66,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 +88,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/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/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/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index 13e3dc6d7c..ff2eac0f30 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -80,16 +80,19 @@ namespace osu.Game.Screens.OnlinePlay.Components { base.LoadComplete(); - Playlist.BindCollectionChanged((_, _) => updateRange()); - room.PropertyChanged += onRoomPropertyChanged; updateRange(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.DifficultyRange)) - updateRange(); + switch (e.PropertyName) + { + case nameof(Room.Playlist): + case nameof(Room.DifficultyRange): + updateRange(); + break; + } } private void updateRange() @@ -97,7 +100,7 @@ namespace osu.Game.Screens.OnlinePlay.Components StarDifficulty minDifficulty; StarDifficulty maxDifficulty; - if (room.DifficultyRange != 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(room.DifficultyRange.Min, 0); @@ -106,7 +109,7 @@ namespace osu.Game.Screens.OnlinePlay.Components 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); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 7e530993dd..70ddf15abf 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -26,24 +26,27 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - Playlist.BindCollectionChanged((_, _) => updateCount()); - room.PropertyChanged += onRoomPropertyChanged; updateCount(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(Room.PlaylistItemStats)) - updateCount(); + switch (e.PropertyName) + { + case nameof(Room.Playlist): + case nameof(Room.PlaylistItemStats): + updateCount(); + break; + } } private void updateCount() { - int activeItems = Playlist.Count > 0 || room.PlaylistItemStats == 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) + ? room.Playlist.Count(i => !i.Expired) : room.PlaylistItemStats.CountActive; TextFlow.Clear(); 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/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index f883fde600..ec257a3d1b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -277,7 +278,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match drawablePlaylist = new DrawableRoomPlaylist { RelativeSizeAxes = Axes.X, - Height = DrawableRoomPlaylistItem.HEIGHT + Height = DrawableRoomPlaylistItem.HEIGHT, + SelectedItem = { BindTarget = SelectedItem } }, selectBeatmapButton = new RoundedButton { @@ -365,9 +367,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.LoadComplete(); - drawablePlaylist.Items.BindTo(Playlist); - drawablePlaylist.SelectedItem.BindTo(SelectedItem); - room.PropertyChanged += onRoomPropertyChanged; updateRoomName(); @@ -377,6 +376,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match updateRoomAutoSkip(); updateRoomMaxParticipants(); updateRoomAutoStartDuration(); + updateRoomPlaylist(); + + drawablePlaylist.Items.BindCollectionChanged((_, __) => room.Playlist = drawablePlaylist.Items.ToArray()); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -410,6 +412,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case nameof(Room.AutoStartDuration): updateRoomAutoStartDuration(); break; + + case nameof(Room.Playlist): + updateRoomPlaylist(); + break; } } @@ -434,11 +440,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void updateRoomAutoStartDuration() => typeLabel.Text = room.AutoStartDuration.GetLocalisableDescription(); + private void updateRoomPlaylist() + => drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist); + protected override void Update() { base.Update(); - ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value; + ApplyButton.Enabled.Value = room.Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value; playlistContainer.Alpha = room.RoomID == null ? 1 : 0; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs index a44891e934..9feee0ae41 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerPlaylist.cs @@ -33,12 +33,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist [Resolved] private MultiplayerClient client { get; set; } = null!; + private readonly Room room; private readonly BindableWithCurrent selectedItem = new BindableWithCurrent(); private MultiplayerPlaylistTabControl playlistTabControl = null!; private MultiplayerQueueList queueList = null!; private MultiplayerHistoryList historyList = null!; private bool firstPopulation = true; + public MultiplayerPlaylist(Room room) + { + this.room = room; + } + [BackgroundDependencyLoader] private void load() { @@ -59,7 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist Masking = true, Children = new Drawable[] { - queueList = new MultiplayerQueueList + queueList = new MultiplayerQueueList(room) { RelativeSizeAxes = Axes.Both, SelectedItem = { BindTarget = selectedItem }, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs index 77d82c4347..04bb9b69e6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/Playlist/MultiplayerQueueList.cs @@ -1,12 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Online.API; @@ -21,28 +20,49 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist /// public partial class MultiplayerQueueList : DrawableRoomPlaylist { - public MultiplayerQueueList() + private readonly Room room; + + private QueueFillFlowContainer flow = null!; + + public MultiplayerQueueList(Room room) { + this.room = room; ShowItemOwners = true; } - protected override FillFlowContainer> CreateListFillFlowContainer() => new QueueFillFlowContainer + protected override void LoadComplete() + { + base.LoadComplete(); + + room.PropertyChanged += onRoomPropertyChanged; + updateRoomPlaylist(); + } + + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Room.Playlist)) + updateRoomPlaylist(); + } + + private void updateRoomPlaylist() + => flow.InvalidateLayout(); + + protected override FillFlowContainer> CreateListFillFlowContainer() => flow = new QueueFillFlowContainer { Spacing = new Vector2(0, 2) }; protected override DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new QueuePlaylistItem(item); + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + room.PropertyChanged -= onRoomPropertyChanged; + } + private partial class QueueFillFlowContainer : FillFlowContainer> { - [Resolved(typeof(Room), nameof(Room.Playlist))] - private BindableList roomPlaylist { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - roomPlaylist.BindCollectionChanged((_, _) => InvalidateLayout()); - } + public new void InvalidateLayout() => base.InvalidateLayout(); public override IEnumerable FlowingChildren => base.FlowingChildren.OfType>().OrderBy(item => item.Model.PlaylistOrder); } @@ -50,10 +70,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist private partial class QueuePlaylistItem : DrawableRoomPlaylistItem { [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; public QueuePlaylistItem(PlaylistItem item) : base(item) @@ -91,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist { base.Dispose(isDisposing); - if (multiplayerClient != null) + if (multiplayerClient.IsNotNull()) multiplayerClient.RoomUpdated -= onRoomUpdated; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 873a1b0d50..4e03c19095 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -24,6 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private OngoingOperationTracker operationTracker { get; set; } = null!; + private readonly Room room; private readonly IBindable operationInProgress = new Bindable(); private readonly PlaylistItem? itemToEdit; @@ -38,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSongSelect(Room room, PlaylistItem? itemToEdit = null) : base(room, itemToEdit) { + this.room = room; this.itemToEdit = itemToEdit; } @@ -111,8 +113,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } else { - Playlist.Clear(); - Playlist.Add(item); + room.Playlist = [item]; this.Exit(); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 8f2f3aa678..65355f02e5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -138,7 +138,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer null, new Drawable[] { - new MultiplayerPlaylist + new MultiplayerPlaylist(Room) { RelativeSizeAxes = Axes.Both, RequestEdit = OpenSongSelection, diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index ffe36d7609..3b0e974a90 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.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. -using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Online.Rooms; @@ -11,9 +9,5 @@ namespace osu.Game.Screens.OnlinePlay /// /// A that exposes bindables for properties. /// - public partial class OnlinePlayComposite : CompositeDrawable - { - [Resolved(typeof(Room))] - protected BindableList Playlist { get; private set; } = null!; - } + public partial class OnlinePlayComposite : CompositeDrawable; } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index a8dfece916..f6b6dfd3ab 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -32,9 +32,6 @@ namespace osu.Game.Screens.OnlinePlay public override bool AllowEditing => false; - [Resolved(typeof(Room), nameof(Room.Playlist))] - protected BindableList Playlist { get; private set; } = null!; - [Resolved] private RulesetStore rulesets { get; set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 6550b4c2f1..468c284d95 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -329,9 +329,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists localUser = api.LocalUser.GetBoundCopy(); localUser.BindValueChanged(populateDurations, true); - - playlist.Items.BindTo(Playlist); - Playlist.BindCollectionChanged(onPlaylistChanged, true); } protected override void LoadComplete() @@ -345,6 +342,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists updateRoomMaxParticipants(); updateRoomDuration(); updateRoomMaxAttempts(); + updateRoomPlaylist(); + + playlist.Items.BindCollectionChanged((_, __) => room.Playlist = playlist.Items.ToArray()); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -370,6 +370,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.MaxAttempts): updateRoomMaxAttempts(); break; + + case nameof(Room.Playlist): + updateRoomPlaylist(); + break; } } @@ -388,6 +392,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomMaxAttempts() => MaxAttemptsField.Text = room.MaxAttempts?.ToString(); + private void updateRoomPlaylist() + => playlist.Items.ReplaceRange(0, playlist.Items.Count, room.Playlist); + private void populateDurations(ValueChangedEvent user) { // roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427) @@ -421,9 +428,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists public void SelectBeatmap() => editPlaylistButton.TriggerClick(); private void onPlaylistChanged(object? sender, NotifyCollectionChangedEventArgs e) => - playlistLength.Text = $"Length: {Playlist.GetTotalDuration(rulesets)}"; + playlistLength.Text = $"Length: {room.Playlist.GetTotalDuration(rulesets)}"; - private bool hasValidSettings => room.RoomID == null && NameField.Text.Length > 0 && Playlist.Count > 0 + private bool hasValidSettings => room.RoomID == null && NameField.Text.Length > 0 && room.Playlist.Count > 0 && hasValidDuration; private bool hasValidDuration => DurationField.Current.Value <= TimeSpan.FromDays(14) || localUser.Value.IsSupporter; @@ -464,7 +471,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists .Select(int.Parse) .ToArray(); - foreach (var item in Playlist) + foreach (var item in room.Playlist) { if (invalidBeatmapIDs.Contains(item.Beatmap.OnlineID)) item.MarkInvalid(); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 079e3b03b7..c82317add4 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -38,6 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private MatchLeaderboard leaderboard = null!; private SelectionPollingComponent selectionPollingComponent = null!; private FillFlowContainer progressSection = null!; + private DrawableRoomPlaylist drawablePlaylist = null!; public PlaylistsRoomSubScreen(Room room) : base(room, false) // Editing is temporarily not allowed. @@ -77,6 +78,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.MaxAttempts): updateRoomMaxAttempts(); break; + + case nameof(Room.Playlist): + updateRoomPlaylist(); + break; } } @@ -93,6 +98,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void updateRoomMaxAttempts() => progressSection.Alpha = Room.MaxAttempts != null ? 1 : 0; + private void updateRoomPlaylist() + => drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, Room.Playlist); + protected override Drawable CreateMainContent() => new Container { RelativeSizeAxes = Axes.Both, @@ -122,13 +130,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Padding = new MarginPadding { Right = 5 }, Content = new[] { - new Drawable[] { new OverlinedPlaylistHeader(), }, + new Drawable[] { new OverlinedPlaylistHeader(Room), }, new Drawable[] { - new DrawableRoomPlaylist + drawablePlaylist = new DrawableRoomPlaylist { RelativeSizeAxes = Axes.Both, - Items = { BindTarget = Room.Playlist }, SelectedItem = { BindTarget = SelectedItem }, AllowSelection = true, AllowShowingResults = true, diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index cedea4af70..23824b6a73 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -12,45 +12,34 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { public partial class PlaylistsSongSelect : OnlinePlaySongSelect { + private readonly Room room; + public PlaylistsSongSelect(Room room) : base(room) { + this.room = room; } - protected override BeatmapDetailArea CreateBeatmapDetailArea() => new MatchBeatmapDetailArea + protected override BeatmapDetailArea CreateBeatmapDetailArea() => new MatchBeatmapDetailArea(room) { - CreateNewItem = createNewItem + CreateNewItem = () => room.Playlist = room.Playlist.Append(createNewItem()).ToArray() }; protected override bool SelectItem(PlaylistItem item) { - switch (Playlist.Count) - { - case 0: - createNewItem(); - break; - - case 1: - Playlist.Clear(); - createNewItem(); - break; - } + if (room.Playlist.Count <= 1) + room.Playlist = [createNewItem()]; this.Exit(); return true; } - private void createNewItem() + private PlaylistItem createNewItem() => new PlaylistItem(Beatmap.Value.BeatmapInfo) { - PlaylistItem item = new PlaylistItem(Beatmap.Value.BeatmapInfo) - { - ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1, - RulesetID = Ruleset.Value.OnlineID, - RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), - AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() - }; - - Playlist.Add(item); - } + ID = room.Playlist.Count == 0 ? 0 : room.Playlist.Max(p => p.ID) + 1, + RulesetID = Ruleset.Value.OnlineID, + RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), + AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() + }; } } diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 774b1d5931..42cf317829 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -38,12 +38,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = "test name", Type = MatchType.HeadToHead, Playlist = - { + [ new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) { RulesetID = Ruleset.Value.OnlineID } - } + ] }; } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 29e62cb590..4d812abf11 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -447,7 +447,7 @@ namespace osu.Game.Tests.Visual.Multiplayer item.PlaylistOrder = existingItem.PlaylistOrder; ServerRoom.Playlist[ServerRoom.Playlist.IndexOf(existingItem)] = item; - ServerAPIRoom.Playlist[ServerAPIRoom.Playlist.IndexOf(ServerAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item); + ServerAPIRoom.Playlist = ServerAPIRoom.Playlist.Select((pi, i) => pi.ID == item.ID ? new PlaylistItem(item) : ServerAPIRoom.Playlist[i]).ToArray(); await ((IMultiplayerClient)this).PlaylistItemChanged(clone(item)).ConfigureAwait(false); } @@ -474,7 +474,7 @@ namespace osu.Game.Tests.Visual.Multiplayer throw new InvalidOperationException("Attempted to remove an item which has already been played."); ServerRoom.Playlist.Remove(item); - ServerAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID); + ServerAPIRoom.Playlist = ServerAPIRoom.Playlist.Where(i => i.ID != item.ID).ToArray(); await ((IMultiplayerClient)this).PlaylistItemRemoved(clone(playlistItemId)).ConfigureAwait(false); await updateCurrentItem(ServerRoom).ConfigureAwait(false); @@ -569,7 +569,7 @@ namespace osu.Game.Tests.Visual.Multiplayer item.ID = ++lastPlaylistItemId; ServerRoom.Playlist.Add(item); - ServerAPIRoom.Playlist.Add(new PlaylistItem(item)); + ServerAPIRoom.Playlist = ServerAPIRoom.Playlist.Append(new PlaylistItem(item)).ToArray(); await ((IMultiplayerClient)this).PlaylistItemAdded(clone(item)).ConfigureAwait(false); await updatePlaylistOrder(ServerRoom).ConfigureAwait(false); diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index a384a14441..e813b49844 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -48,10 +48,13 @@ namespace osu.Game.Tests.Visual.OnlinePlay RulesetIDs = new[] { ruleset.OnlineID }, }; - room.Playlist.Add(new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) - { - RulesetID = ruleset.OnlineID, - }); + room.Playlist = + [ + new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) + { + RulesetID = ruleset.OnlineID, + } + ]; } CreateRoom(room); diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index 37325c2d6b..224bb90c8c 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -290,19 +290,17 @@ namespace osu.Game.Tests.Visual.OnlinePlay var result = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source)); Debug.Assert(result != null); - // Playlist item IDs and beatmaps aren't serialised. - if (source.CurrentPlaylistItem != null) - { - Debug.Assert(result.CurrentPlaylistItem != null); - result.CurrentPlaylistItem = result.CurrentPlaylistItem.With(new Optional(source.CurrentPlaylistItem.Beatmap)); - result.CurrentPlaylistItem.ID = source.CurrentPlaylistItem.ID; - } + // When serialising, only beatmap IDs are sent to the server. + // When deserialising, full beatmaps and IDs are expected to arrive. - for (int i = 0; i < source.Playlist.Count; i++) - { - result.Playlist[i] = result.Playlist[i].With(new Optional(source.Playlist[i].Beatmap)); - result.Playlist[i].ID = source.Playlist[i].ID; - } + PlaylistItem? finalCurrentItem = result.CurrentPlaylistItem?.With(id: source.CurrentPlaylistItem!.ID, beatmap: new Optional(source.CurrentPlaylistItem.Beatmap)); + PlaylistItem[] finalPlaylist = result.Playlist.Select((pi, i) => pi.With(id: source.Playlist[i].ID, beatmap: new Optional(source.Playlist[i].Beatmap))).ToArray(); + + // When setting the properties, we do a clear-then-add, otherwise equality comparers (that only compare by ID) pass early and members don't get replaced. + result.CurrentPlaylistItem = null; + result.CurrentPlaylistItem = finalCurrentItem; + result.Playlist = []; + result.Playlist = finalPlaylist; return result; } From bfbae9458a629e0afa2cec4784fb367883019e45 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Nov 2024 13:20:09 +0900 Subject: [PATCH 34/73] Remove `OnlinePlayComposite` --- .../Screens/OnlinePlay/Components/BeatmapTitle.cs | 3 ++- .../OnlinePlay/Components/OverlinedHeader.cs | 2 +- .../Components/ParticipantCountDisplay.cs | 2 +- .../OnlinePlay/Components/ParticipantsDisplay.cs | 3 ++- .../OnlinePlay/Components/ParticipantsList.cs | 2 +- .../OnlinePlay/Components/RoomLocalUserInfo.cs | 2 +- .../OnlinePlay/Components/StarRatingRangeDisplay.cs | 2 +- .../DailyChallengeTimeRemainingRing.cs | 2 +- .../OnlinePlay/Lounge/Components/DrawableRoom.cs | 4 ++-- .../Components/DrawableRoomParticipantsList.cs | 2 +- .../OnlinePlay/Lounge/Components/EndDateInfo.cs | 3 ++- .../OnlinePlay/Lounge/Components/OnlinePlayPill.cs | 3 ++- .../Match/Components/RoomSettingsOverlay.cs | 4 ++-- .../Match/MultiplayerMatchSettingsOverlay.cs | 4 ++-- osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 13 ------------- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 4 ++-- 16 files changed, 23 insertions(+), 32 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index 01b71539fe..5c8ac5ce73 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs @@ -5,6 +5,7 @@ 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; @@ -13,7 +14,7 @@ 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; 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/ParticipantCountDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs index f9744717d5..db9cf3f92d 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantCountDisplay.cs @@ -11,7 +11,7 @@ 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; diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs index 5a040dcadb..a12d843b0a 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsDisplay.cs @@ -4,12 +4,13 @@ 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 readonly Bindable Details = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs index 1be23014bd..79084a5285 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ParticipantsList.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Components { - public partial class ParticipantsList : OnlinePlayComposite + public partial class ParticipantsList : CompositeDrawable { public const float TILE_SIZE = 35; diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs index b3e60d4318..39b5edbd26 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs @@ -12,7 +12,7 @@ using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { - public partial class RoomLocalUserInfo : OnlinePlayComposite + public partial class RoomLocalUserInfo : CompositeDrawable { private readonly Room room; private OsuSpriteText attemptDisplay = null!; diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index ff2eac0f30..2bdb41ce12 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -18,7 +18,7 @@ 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; diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs index bffbb664a3..bf01ee6b52 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeTimeRemainingRing.cs @@ -17,7 +17,7 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.DailyChallenge { - public partial class DailyChallengeTimeRemainingRing : OnlinePlayComposite + public partial class DailyChallengeTimeRemainingRing : CompositeDrawable { private readonly Room room; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index c368666ac2..2a9bb93d94 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -346,7 +346,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components if (Room.Type != MatchType.Playlists) { - pills.AddRange(new OnlinePlayComposite[] + pills.AddRange(new Drawable[] { new MatchTypePill(Room), new QueueModePill(Room), @@ -377,7 +377,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Room.PropertyChanged -= onRoomPropertyChanged; } - private partial class RoomStatusText : OnlinePlayComposite + private partial class RoomStatusText : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs index d989bd6c82..5bcc974c26 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoomParticipantsList.cs @@ -21,7 +21,7 @@ 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; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs index 18c7972c06..3b03ce61f1 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/EndDateInfo.cs @@ -5,12 +5,13 @@ using System; using System.ComponentModel; using osu.Framework.Allocation; 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 { private readonly Room room; 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/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 14d506a368..09aed176c4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -21,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; } = null!; + protected Drawable Settings { get; set; } = null!; protected override bool BlockScrollInput => false; @@ -48,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/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index ec257a3d1b..1dbef079d4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match protected override void SelectBeatmap() => settings.SelectBeatmap(); - protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room) + protected override Drawable CreateSettings(Room room) => settings = new MatchSettings(room) { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, @@ -59,7 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match SelectedItem = { BindTarget = SelectedItem } }; - protected partial class MatchSettings : OnlinePlayComposite + protected partial class MatchSettings : CompositeDrawable { private const float disabled_alpha = 0.2f; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs deleted file mode 100644 index 3b0e974a90..0000000000 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Containers; -using osu.Game.Online.Rooms; - -namespace osu.Game.Screens.OnlinePlay -{ - /// - /// A that exposes bindables for properties. - /// - public partial class OnlinePlayComposite : CompositeDrawable; -} diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 468c284d95..88af161cc8 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -47,14 +47,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists protected override void SelectBeatmap() => settings.SelectBeatmap(); - protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room) + protected override Drawable CreateSettings(Room room) => settings = new MatchSettings(room) { RelativeSizeAxes = Axes.Both, RelativePositionAxes = Axes.Y, EditPlaylist = () => EditPlaylist?.Invoke() }; - protected partial class MatchSettings : OnlinePlayComposite + protected partial class MatchSettings : CompositeDrawable { private const float disabled_alpha = 0.2f; From 1a656d0ec353897852630bdff3839a8c2105c09d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Nov 2024 13:22:03 +0900 Subject: [PATCH 35/73] Remove `CachedModelDependencyContainer` usages from online play --- .../DailyChallenge/TestSceneDailyChallengeCarousel.cs | 5 ----- .../TestSceneDailyChallengeTimeRemainingRing.cs | 5 ----- .../Visual/Multiplayer/TestSceneMatchStartControl.cs | 3 --- .../Visual/Multiplayer/TestSceneRankRangePill.cs | 5 ----- .../Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs | 8 -------- .../Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs | 8 -------- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 8 -------- .../Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs | 2 +- 8 files changed, 1 insertion(+), 43 deletions(-) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs index 5854c2e793..b9470f3be4 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeCarousel.cs @@ -26,11 +26,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge private readonly Bindable room = new Bindable(new Room()); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) - { - Model = { BindTarget = room } - }; - [Test] public void TestBasicAppearance() { diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs index 2f522d4d6f..eebbd82190 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallengeTimeRemainingRing.cs @@ -18,11 +18,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge { private readonly Bindable room = new Bindable(new Room()); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) - { - Model = { BindTarget = room } - }; - [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 6bb5ca254b..fb9c801fb4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -43,9 +43,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private OsuButton readyButton => control.ChildrenOfType().Single(); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } }; - [BackgroundDependencyLoader] private void load() { 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/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 9559826dab..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) { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index 2a9bb93d94..c39ca347c7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -316,14 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components passwordIcon.Alpha = Room.HasPassword ? 1 : 0; } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) - { - Model = { Value = Room } - }; - } - private int numberOfAvatars = 7; public int NumberOfAvatars diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 1b7df9c213..4461cf5402 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -287,14 +287,6 @@ namespace osu.Game.Screens.OnlinePlay.Match } } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - return new CachedModelDependencyContainer(base.CreateChildDependencies(parent)) - { - Model = { Value = Room } - }; - } - protected virtual bool IsConnected => api.State.Value == APIState.Online; public override bool OnBackButton() diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index 64bd27b871..06f8cc40b9 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay UserLookupCache = new TestUserLookupCache(); BeatmapLookupCache = new BeatmapLookupCache(); - dependencies = new DependencyContainer(new CachedModelDependencyContainer(null) { Model = { BindTarget = SelectedRoom } }); + dependencies = new DependencyContainer(); CacheAs(RequestsHandler); CacheAs(SelectedRoom); From db7def9d344be36176a8b7901ed3171b79731b0e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Nov 2024 16:05:06 +0900 Subject: [PATCH 36/73] Remove `IDependencyInjectionCandidate` interface from `Room` --- osu.Game/Online/Rooms/Room.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index deb5808861..a26a3fcb34 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -7,7 +7,6 @@ using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using Newtonsoft.Json; -using osu.Framework.Allocation; using osu.Game.IO.Serialization.Converters; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; @@ -16,7 +15,7 @@ using osu.Game.Online.Rooms.RoomStatuses; namespace osu.Game.Online.Rooms { [JsonObject(MemberSerialization.OptIn)] - public partial class Room : IDependencyInjectionCandidate, INotifyPropertyChanged + public partial class Room : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; From b014bfea3e96f5195ef0822db094866d0d7aedc0 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 17 Nov 2024 09:56:19 -0500 Subject: [PATCH 37/73] Fix key counter not updating activation state on initial load --- osu.Game/Screens/Play/HUD/InputTrigger.cs | 7 +++++++ osu.Game/Screens/Play/HUD/KeyCounter.cs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/InputTrigger.cs b/osu.Game/Screens/Play/HUD/InputTrigger.cs index edc61ec142..72dade25f6 100644 --- a/osu.Game/Screens/Play/HUD/InputTrigger.cs +++ b/osu.Game/Screens/Play/HUD/InputTrigger.cs @@ -34,6 +34,11 @@ namespace osu.Game.Screens.Play.HUD /// public IBindable ActivationCount => activationCount; + /// + /// Whether this is currently active. + /// + public bool IsActive { get; private set; } + /// /// Whether any activation or deactivation of this impacts its /// @@ -49,6 +54,7 @@ namespace osu.Game.Screens.Play.HUD if (forwardPlayback && isCounting.Value) activationCount.Value++; + IsActive = true; OnActivate?.Invoke(forwardPlayback); } @@ -57,6 +63,7 @@ namespace osu.Game.Screens.Play.HUD if (!forwardPlayback && isCounting.Value) activationCount.Value--; + IsActive = false; OnDeactivate?.Invoke(forwardPlayback); } } diff --git a/osu.Game/Screens/Play/HUD/KeyCounter.cs b/osu.Game/Screens/Play/HUD/KeyCounter.cs index 66f9dfd6f2..b506694044 100644 --- a/osu.Game/Screens/Play/HUD/KeyCounter.cs +++ b/osu.Game/Screens/Play/HUD/KeyCounter.cs @@ -36,6 +36,14 @@ namespace osu.Game.Screens.Play.HUD Trigger.OnDeactivate += Deactivate; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (Trigger.IsActive) + Activate(); + } + protected virtual void Activate(bool forwardPlayback = true) { isActive.Value = true; From e5778eb1fc85ee3f12594e504e4aa001210d95a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Nov 2024 14:38:12 +0900 Subject: [PATCH 38/73] Add scrollbar to manage collections dialog --- osu.Game/Collections/DrawableCollectionList.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs index 164ec558a4..441b7fabed 100644 --- a/osu.Game/Collections/DrawableCollectionList.cs +++ b/osu.Game/Collections/DrawableCollectionList.cs @@ -104,8 +104,8 @@ namespace osu.Game.Collections public Scroll() { - ScrollbarVisible = false; Padding = new MarginPadding(10); + ScrollbarOverlapsContent = false; base.Content.Add(new FillFlowContainer { @@ -187,6 +187,8 @@ namespace osu.Game.Collections { Spacing = new Vector2(0, 5); LayoutEasing = Easing.OutQuint; + + Padding = new MarginPadding { Right = 5 }; } protected override void LoadComplete() From a570863854ba794c4d9c72e84656568ce9c37d1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Nov 2024 15:12:28 +0900 Subject: [PATCH 39/73] Scroll to new collection in list when adding a new collection --- .../Collections/DrawableCollectionList.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs index 441b7fabed..5096dce55c 100644 --- a/osu.Game/Collections/DrawableCollectionList.cs +++ b/osu.Game/Collections/DrawableCollectionList.cs @@ -46,6 +46,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 +80,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]; From 5ce1f7679be37328fb74b456fa95bfa0540e4271 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Nov 2024 15:57:39 +0900 Subject: [PATCH 40/73] Add search bar to collection management dialog --- .../TestSceneManageCollectionsDialog.cs | 40 +++++++++++++++++-- .../Collections/DrawableCollectionList.cs | 11 +++-- .../Collections/DrawableCollectionListItem.cs | 24 ++++++++++- .../Collections/ManageCollectionsDialog.cs | 37 ++++++++++++++++- 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index 56e7b4d39f..0f2f716a07 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Collections; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; using osu.Game.Rulesets; @@ -265,7 +266,6 @@ namespace osu.Game.Tests.Visual.Collections } [Test] - [Solo] public void TestCollectionRenamedExternal() { BeatmapCollection first = null!; @@ -338,10 +338,44 @@ namespace osu.Game.Tests.Visual.Collections AddUntilStep("collection has new name", () => first.Name == "First"); } + [Test] + public void TestSearch() + { + BeatmapCollection first = null!; + + AddStep("add two collections", () => + { + Realm.Write(r => + { + r.Add(new[] + { + first = new BeatmapCollection(name: "1"), + new BeatmapCollection(name: "2"), + }); + }); + }); + + assertCollectionName(0, "1"); + assertCollectionName(1, "2"); + + AddStep("search for 1", () => dialog.ChildrenOfType().Single().Current.Value = "1"); + + assertCollectionCount(1); + + AddStep("change first collection name", () => Realm.Write(_ => first.Name = "First")); + + assertCollectionCount(0); + + AddStep("search for first", () => dialog.ChildrenOfType().Single().Current.Value = "firs"); + + assertCollectionCount(1); + } + private void assertCollectionCount(int count) - => AddUntilStep($"{count} collections shown", () => dialog.ChildrenOfType().Count() == count + 1); // +1 for placeholder + => AddUntilStep($"{count} collections shown", () => dialog.ChildrenOfType().Count(i => i.IsPresent) == count + 1); // +1 for placeholder private void assertCollectionName(int index, string name) - => AddUntilStep($"item {index + 1} has correct name", () => dialog.ChildrenOfType().Single().OrderedItems.ElementAt(index).ChildrenOfType().First().Text == name); + => AddUntilStep($"item {index + 1} has correct name", + () => dialog.ChildrenOfType().Single().OrderedItems.ElementAt(index).ChildrenOfType().First().Text == name); } } diff --git a/osu.Game/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs index 5096dce55c..f00691ba0f 100644 --- a/osu.Game/Collections/DrawableCollectionList.cs +++ b/osu.Game/Collections/DrawableCollectionList.cs @@ -34,6 +34,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 } @@ -128,7 +134,6 @@ namespace osu.Game.Collections public Scroll() { - Padding = new MarginPadding(10); ScrollbarOverlapsContent = false; base.Content.Add(new FillFlowContainer @@ -157,7 +162,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); } /// @@ -203,7 +208,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(); 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..90b73bc6b2 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,29 @@ namespace osu.Game.Collections RelativeSizeAxes = Axes.Both, Colour = colours.GreySeaFoamDarker }, - new DrawableCollectionList + new FillFlowContainer { RelativeSizeAxes = Axes.Both, - } + Direction = FillDirection.Vertical, + Padding = new MarginPadding(10), + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + searchTextBox = new BasicSearchTextBox + { + RelativeSizeAxes = Axes.X, + Y = 10, + Height = 40, + ReleaseFocusOnCommit = false, + HoldFocus = true, + PlaceholderText = HomeStrings.SearchPlaceholder, + }, + list = new DrawableCollectionList + { + RelativeSizeAxes = Axes.Both, + } + } + }, } } }, @@ -117,6 +140,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); From ebc2cc35700415a2b242c03f65880d7371278462 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Nov 2024 16:51:34 +0900 Subject: [PATCH 41/73] Ensure cleanup tasks are run even on a cancelled / exceptioned import task --- osu.Game/Database/ModelDownloader.cs | 25 ++-- .../Database/RealmArchiveModelImporter.cs | 108 +++++++++--------- 2 files changed, 72 insertions(+), 61 deletions(-) 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 22bcc622e9..e538530b79 100644 --- a/osu.Game/Database/RealmArchiveModelImporter.cs +++ b/osu.Game/Database/RealmArchiveModelImporter.cs @@ -117,67 +117,73 @@ namespace osu.Game.Database await pauseIfNecessaryAsync(parameters, notification, notification.CancellationToken).ConfigureAwait(false); - await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) => + try { - cancellation.ThrowIfCancellationRequested(); - - try + await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) => { - await pauseIfNecessaryAsync(parameters, notification, cancellation).ConfigureAwait(false); + cancellation.ThrowIfCancellationRequested(); - var model = await Import(task, parameters, cancellation).ConfigureAwait(false); - - 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) - { - throw; - } - 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; From 02deb393351e94e6778281e76bf0a345b0cb70a2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 18 Nov 2024 17:20:11 +0900 Subject: [PATCH 42/73] Make android build on non-maui workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 15a8cfe685a7e16381ed458fb5cdbe1b90c37f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Nov 2024 14:30:17 +0100 Subject: [PATCH 43/73] Fix mania notes disappearing on seek to their end time --- osu.Game.Rulesets.Mania/Edit/EditorColumn.cs | 45 +++++++++++++++++++ osu.Game.Rulesets.Mania/Edit/EditorStage.cs | 18 ++++++++ .../Edit/ManiaEditorPlayfield.cs | 3 ++ osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 6 ++- osu.Game.Rulesets.Mania/UI/Stage.cs | 16 ++++--- 5 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Edit/EditorColumn.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/EditorStage.cs diff --git a/osu.Game.Rulesets.Mania/Edit/EditorColumn.cs b/osu.Game.Rulesets.Mania/Edit/EditorColumn.cs new file mode 100644 index 0000000000..11d1848173 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/EditorColumn.cs @@ -0,0 +1,45 @@ +// 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.Graphics; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public partial class EditorColumn : Column + { + public EditorColumn(int index, bool isSpecial) + : base(index, isSpecial) + { + } + + protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) + { + base.OnNewDrawableHitObject(drawableHitObject); + drawableHitObject.ApplyCustomUpdateState += (dho, state) => + { + switch (dho) + { + // hold note heads are exempt from what follows due to the "freezing" mechanic + // which already ensures they'll never fade away on their own. + case DrawableHoldNoteHead: + break; + + // mania features instantaneous hitobject fade-outs. + // this means that without manual intervention stopping the clock at the precise time of hitting the object + // means the object will fade out. + // this is anti-user in editor contexts, as the user is expecting to continue the see the note on the receptor line. + // therefore, apply a crude workaround to prevent it from going away. + default: + { + if (state == ArmedState.Hit) + dho.FadeTo(1).Delay(1).FadeOut().Expire(); + break; + } + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/EditorStage.cs b/osu.Game.Rulesets.Mania/Edit/EditorStage.cs new file mode 100644 index 0000000000..c5f93f6182 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/EditorStage.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.UI; + +namespace osu.Game.Rulesets.Mania.Edit +{ + public partial class EditorStage : Stage + { + public EditorStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction columnStartAction) + : base(firstColumnIndex, definition, ref columnStartAction) + { + } + + protected override Column CreateColumn(int index, bool isSpecial) => new EditorColumn(index, isSpecial); + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs index 77e372d1d6..2dc2b8ae48 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditorPlayfield.cs @@ -13,5 +13,8 @@ namespace osu.Game.Rulesets.Mania.Edit : base(stages) { } + + protected override Stage CreateStage(int firstColumnIndex, StageDefinition stageDefinition, ref ManiaAction columnAction) + => new EditorStage(firstColumnIndex, stageDefinition, ref columnAction); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 1f388144bd..a4ebb3347a 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using System; using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Mania.Beatmaps; @@ -71,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.UI for (int i = 0; i < stageDefinitions.Count; i++) { - var newStage = new Stage(firstColumnIndex, stageDefinitions[i], ref columnAction); + var newStage = CreateStage(firstColumnIndex, stageDefinitions[i], ref columnAction); playfieldGrid.Content[0][i] = newStage; @@ -82,6 +83,9 @@ namespace osu.Game.Rulesets.Mania.UI } } + [Pure] + protected virtual Stage CreateStage(int firstColumnIndex, StageDefinition stageDefinition, ref ManiaAction columnAction) => new Stage(firstColumnIndex, stageDefinition, ref columnAction); + public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject); public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject); diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index 86f2243561..9fb77a4995 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -134,12 +135,14 @@ namespace osu.Game.Rulesets.Mania.UI { bool isSpecial = definition.IsSpecialColumn(i); - var column = new Column(firstColumnIndex + i, isSpecial) + var action = columnStartAction; + columnStartAction++; + var column = CreateColumn(firstColumnIndex + i, isSpecial).With(c => { - RelativeSizeAxes = Axes.Both, - Width = 1, - Action = { Value = columnStartAction++ } - }; + c.RelativeSizeAxes = Axes.Both; + c.Width = 1; + c.Action.Value = action; + }); topLevelContainer.Add(column.TopLevelContainer.CreateProxy()); columnBackgrounds.Add(column.BackgroundContainer.CreateProxy()); @@ -154,6 +157,9 @@ namespace osu.Game.Rulesets.Mania.UI RegisterPool(50, 200); } + [Pure] + protected virtual Column CreateColumn(int index, bool isSpecial) => new Column(index, isSpecial); + [BackgroundDependencyLoader] private void load(ISkinSource skin) { From 259e9ecf65704068e0745afbb6c6d312a90f5339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Nov 2024 15:06:13 +0100 Subject: [PATCH 44/73] Deactivate new combo toggle on deselecting objects Closes https://github.com/ppy/osu/issues/30713. Was a point of discussion doing review: https://github.com/ppy/osu/pull/30214#discussion_r1798833139 Given it got pointed out immediately for something so minor, I'm inclined to believe it's a rather undesirable change. --- .../Screens/Edit/Compose/Components/EditorSelectionHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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; From 22b082d968d7d4179de0d0350867a0c0f187f377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Nov 2024 15:28:30 +0100 Subject: [PATCH 45/73] Fix not being able to scroll to new collection text box when list overflows --- osu.Game/Collections/DrawableCollectionList.cs | 6 ++++++ osu.Game/Collections/ManageCollectionsDialog.cs | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Collections/DrawableCollectionList.cs b/osu.Game/Collections/DrawableCollectionList.cs index f00691ba0f..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] diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs index 90b73bc6b2..a738ae66cb 100644 --- a/osu.Game/Collections/ManageCollectionsDialog.cs +++ b/osu.Game/Collections/ManageCollectionsDialog.cs @@ -108,12 +108,10 @@ namespace osu.Game.Collections RelativeSizeAxes = Axes.Both, Colour = colours.GreySeaFoamDarker }, - new FillFlowContainer + new Container { RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, Padding = new MarginPadding(10), - Spacing = new Vector2(0, 10), Children = new Drawable[] { searchTextBox = new BasicSearchTextBox @@ -127,6 +125,10 @@ namespace osu.Game.Collections }, list = new DrawableCollectionList { + Padding = new MarginPadding + { + Top = 60, + }, RelativeSizeAxes = Axes.Both, } } From a2af4cbb50f04cd528d01f3c5becee0daeadd080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Nov 2024 23:33:48 +0900 Subject: [PATCH 46/73] Fix right click scroll at song select not quite matching scrollbar position Closes https://github.com/ppy/osu/issues/30744. --- osu.Game/Graphics/Containers/OsuScrollContainer.cs | 13 +++++++++---- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) 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/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 95268c35da..5e1e0ce615 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -245,8 +245,7 @@ namespace osu.Game.Screens.Select config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); - RightClickScrollingEnabled.ValueChanged += enabled => Scroll.RightMouseScrollbar = enabled.NewValue; - RightClickScrollingEnabled.TriggerChange(); + RightClickScrollingEnabled.BindValueChanged(enabled => Scroll.RightMouseScrollbar = enabled.NewValue, true); if (detachedBeatmapStore != null && detachedBeatmapSets == null) { From 16158710f9567a1641c272dd21f6d70a33d8338e Mon Sep 17 00:00:00 2001 From: Darius Wattimena Date: Mon, 18 Nov 2024 23:48:26 +0100 Subject: [PATCH 47/73] Made reading of ReadCurrentDistanceSnap public --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapProvider.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapProvider.cs | 2 +- osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapProvider.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapProvider.cs index 6f5b32a41d..ae4025aa2f 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapProvider.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapProvider.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Edit { public partial class CatchDistanceSnapProvider : ComposerDistanceSnapProvider { - protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) + public override double ReadCurrentDistanceSnap(HitObject before, HitObject after) { // osu!catch's distance snap implementation is limited, in that a custom spacing cannot be specified. // Therefore this functionality is not currently used. diff --git a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapProvider.cs index 522943df7d..4042cfa0e2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapProvider.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuDistanceSnapProvider : ComposerDistanceSnapProvider { - protected override double ReadCurrentDistanceSnap(HitObject before, HitObject after) + public override double ReadCurrentDistanceSnap(HitObject before, HitObject after) { float expectedDistance = DurationToDistance(before, after.StartTime - before.GetEndTime()); float actualDistance = Vector2.Distance(((OsuHitObject)before).EndPosition, ((OsuHitObject)after).Position); 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() { From 111f029ead5d5130904bcae1e463d0a330039eec Mon Sep 17 00:00:00 2001 From: Darius Wattimena Date: Mon, 18 Nov 2024 23:53:01 +0100 Subject: [PATCH 48/73] Added a custom hitobject inspector for catch additionally show the x value of prev and next --- .../Edit/CatchHitObjectComposer.cs | 2 + .../Edit/CatchHitObjectInspector.cs | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 09d1ff663d..aae3369d40 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -70,6 +70,8 @@ namespace osu.Game.Rulesets.Catch.Edit })); } + protected override Drawable CreateHitObjectInspector() => new CatchHitObjectInspector(DistanceSnapProvider); + protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons() .Concat(DistanceSnapProvider.CreateTernaryButtons()); diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs new file mode 100644 index 0000000000..6ff7e9c6a0 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs @@ -0,0 +1,43 @@ +// 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 osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit.Compose.Components; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public partial class CatchHitObjectInspector(CatchDistanceSnapProvider snapProvider) : HitObjectInspector + { + protected override void AddInspectorValues(HitObject[] objects) + { + base.AddInspectorValues(objects); + + if (objects.Length > 0) + { + HitObject firstSelectedHitObject = objects.MinBy(ho => ho.StartTime)!; + HitObject lastSelectedHitObject = objects.MaxBy(ho => ho.GetEndTime())!; + + HitObject? precedingObject = EditorBeatmap.HitObjects.LastOrDefault(ho => ho.GetEndTime() < firstSelectedHitObject.StartTime); + HitObject? nextObject = EditorBeatmap.HitObjects.FirstOrDefault(ho => ho.StartTime > lastSelectedHitObject.GetEndTime()); + + if (precedingObject != null || nextObject != null) + { + AddHeader("Snapping"); + } + + if (precedingObject != null) + { + double previousSnap = snapProvider.ReadCurrentDistanceSnap(precedingObject, firstSelectedHitObject); + AddValue($"Previous: {previousSnap:#,0.##}x"); + } + + if (nextObject != null) + { + double nextSnap = snapProvider.ReadCurrentDistanceSnap(lastSelectedHitObject, nextObject); + AddValue($"Next: {nextSnap:#,0.##}x"); + } + } + } + } +} From a5327aa5627c42de00de135a1d1928cd044c44bb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Nov 2024 18:48:30 +0900 Subject: [PATCH 49/73] Use properties instead of fields --- .../Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 8ddda485b5..be6ca43f4b 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -19,12 +19,12 @@ namespace osu.Game.Beatmaps.Drawables /// /// Delay before the background is loaded while on-screen. /// - public double BackgroundLoadDelay = 500; + public double BackgroundLoadDelay { get; set; } = 500; /// /// Delay before the background is unloaded while off-screen. /// - public double BackgroundUnloadDelay = 10000; + public double BackgroundUnloadDelay { get; set; } = 10000; [Resolved] private BeatmapManager beatmaps { get; set; } = null!; From a8e14b6625552e35687bb6bf347898ad5deb552e Mon Sep 17 00:00:00 2001 From: Darius Wattimena Date: Tue, 19 Nov 2024 19:54:12 +0100 Subject: [PATCH 50/73] Align inspector info more to how it is shown for osu --- .../Edit/CatchHitObjectInspector.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs index 6ff7e9c6a0..c279354d9a 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs @@ -21,21 +21,18 @@ namespace osu.Game.Rulesets.Catch.Edit HitObject? precedingObject = EditorBeatmap.HitObjects.LastOrDefault(ho => ho.GetEndTime() < firstSelectedHitObject.StartTime); HitObject? nextObject = EditorBeatmap.HitObjects.FirstOrDefault(ho => ho.StartTime > lastSelectedHitObject.GetEndTime()); - if (precedingObject != null || nextObject != null) - { - AddHeader("Snapping"); - } - if (precedingObject != null) { double previousSnap = snapProvider.ReadCurrentDistanceSnap(precedingObject, firstSelectedHitObject); - AddValue($"Previous: {previousSnap:#,0.##}x"); + AddHeader("To previous"); + AddValue($"{previousSnap:#,0.##}x"); } if (nextObject != null) { double nextSnap = snapProvider.ReadCurrentDistanceSnap(lastSelectedHitObject, nextObject); - AddValue($"Next: {nextSnap:#,0.##}x"); + AddHeader("To next"); + AddValue($"{nextSnap:#,0.##}x"); } } } From 9ac877606b29099112c7b5058833bb408474858b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Nov 2024 12:32:42 +0900 Subject: [PATCH 51/73] Fix inspection issues --- .../TestScenePlaylistsSongSelect.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index fea6617d3f..b55102518a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestItemAddedWhenCreateNewItemClicked() { - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1); } [Test] public void TestItemNotAddedIfExistingOnStart() { - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("finalise selection", () => songSelect.FinaliseSelection()); AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1); } @@ -89,19 +89,19 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestAddSameItemMultipleTimes() { - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2); } [Test] public void TestAddItemAfterRearrangement() { - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("rearrange", () => SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Skip(1).Append(SelectedRoom.Value.Playlist[0]).ToArray()); - AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2); } @@ -112,9 +112,9 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestNewItemHasNewModInstances() { AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() }); - AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2); - AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddAssert("item 1 has rate 1.5", () => { @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Multiplayer mod = (OsuModDoubleTime)SelectedMods.Value[0]; }); - AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem()); + AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2); AddAssert("item has rate 1.5", () => From 50089c027eb9d4c1c1f57314d9ff1e252387f616 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Nov 2024 16:40:33 +0900 Subject: [PATCH 52/73] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index f271bdfaaf..4699beeac0 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index ecb9ae2c8d..ccae4a15ee 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -17,6 +17,6 @@ -all - + From 03de51848437b2fbadb028d2a414de8c936dc6e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Nov 2024 16:56:22 +0900 Subject: [PATCH 53/73] Fix missing `updateRoomPlaylist` call --- osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index c82317add4..f915676fb1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -65,6 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Room.PropertyChanged += onRoomPropertyChanged; updateSetupState(); updateRoomMaxAttempts(); + updateRoomPlaylist(); } private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) From c6d08daee84e188f6fe51c95583869e16b42f209 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Nov 2024 17:11:49 +0900 Subject: [PATCH 54/73] Remove `MultiplayerMatchSubScreen` flaky test attribute Didn't really work to fix these tests due to the sticky nature of the failure. Also I can no longer reproduce locally, so the hope is that these are fixed by https://github.com/ppy/osu/pull/30634. --- .../TestSceneMultiplayerMatchSubScreen.cs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index ef5ff25194..abcb591cf6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -77,25 +77,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] - /* - * Fail rate around 1.5% - * - * TearDown : System.AggregateException : One or more errors occurred. (Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')) - ----> 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", () => @@ -115,7 +96,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestTaikoOnlyMod() { AddStep("add playlist item", () => @@ -139,7 +119,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestSettingValidity() { AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value); @@ -159,7 +138,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestStartMatchWhileSpectating() { AddStep("set playlist", () => @@ -191,7 +169,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestFreeModSelectionHasAllowedMods() { AddStep("add playlist item with allowed mod", () => @@ -221,7 +198,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestModSelectKeyWithAllowedMods() { AddStep("add playlist item with allowed mod", () => @@ -246,7 +222,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestModSelectKeyWithNoAllowedMods() { AddStep("add playlist item with no allowed mods", () => @@ -270,7 +245,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestNextPlaylistItemSelectedAfterCompletion() { AddStep("add two playlist items", () => @@ -307,7 +281,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - [FlakyTest] // See above public void TestModSelectOverlay() { AddStep("add playlist item", () => From 7726ca02b093df019fa56424563417507bf2b760 Mon Sep 17 00:00:00 2001 From: Fivoka <42149652+Fivoka@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:29:29 +0100 Subject: [PATCH 55/73] Changed multiplier from 2 to 3.5 --- osu.Game/Screens/Play/BreakOverlay.cs | 2 +- osu.Game/Screens/Play/SkipOverlay.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 1fdb9402bc..550d29965f 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -166,7 +166,7 @@ namespace osu.Game.Screens.Play return; float timeBoxTargetWidth = (float)Math.Max(0, (remainingTimeForCurrentPeriod - timingPoint.BeatLength / currentPeriod.Value.Value.Duration)); - remainingTimeBox.ResizeWidthTo(timeBoxTargetWidth, timingPoint.BeatLength * 2, Easing.OutQuint); + remainingTimeBox.ResizeWidthTo(timeBoxTargetWidth, timingPoint.BeatLength * 3.5, Easing.OutQuint); } private void updateDisplay(ValueChangedEvent period) diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 362677ca5c..be8517d9a0 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Play float progress = (float)(gameplayClock.CurrentTime - displayTime) / (float)(fadeOutBeginTime - displayTime); float newWidth = 1 - Math.Clamp(progress, 0, 1); - remainingTimeBox.ResizeWidthTo(newWidth, timingPoint.BeatLength * 2, Easing.OutQuint); + remainingTimeBox.ResizeWidthTo(newWidth, timingPoint.BeatLength * 3.5, Easing.OutQuint); } public partial class FadeContainer : Container, IStateful From dfa32302acf14ab470e0827d5efbf7bdeed09d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Nov 2024 13:16:37 +0100 Subject: [PATCH 56/73] Add test case covering stability of compatibility export operation This is important as the format will be used more when lazer beatmap submission comes online, and its stability is a useful property for that. Included archive contains an `.osu` with a few fractional-millisecond timing points and objects, as well as a multi-segment-type slider. That should cover the range of all possible modifications that the compatibility exporter currently performs. --- .../Beatmaps/IO/LegacyBeatmapExporterTest.cs | 43 ++++++++++++++++++ .../Archives/legacy-export-stability-test.olz | Bin 0 -> 946 bytes 2 files changed, 43 insertions(+) create mode 100644 osu.Game.Tests/Resources/Archives/legacy-export-stability-test.olz diff --git a/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs index 9947def06d..8a95d26782 100644 --- a/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.IO; +using System.Text; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; @@ -9,6 +10,7 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.IO.Archives; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual; using MemoryStream = System.IO.MemoryStream; @@ -48,6 +50,47 @@ namespace osu.Game.Tests.Beatmaps.IO AddAssert("hit object is snapped", () => beatmap.Beatmap.HitObjects[0].StartTime, () => Is.EqualTo(28519).Within(0.001)); } + [Test] + public void TestExportStability() + { + IWorkingBeatmap beatmap = null!; + MemoryStream firstExport = null!; + MemoryStream secondExport = null!; + + // Ensure importer encoding is correct + AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"legacy-export-stability-test.olz")); + AddStep("export once", () => + { + firstExport = new MemoryStream(); + + new LegacyBeatmapExporter(LocalStorage) + .ExportToStream((BeatmapSetInfo)beatmap.BeatmapInfo.BeatmapSet!, firstExport, null); + }); + + AddStep("import beatmap again", () => beatmap = importBeatmapFromStream(firstExport)); + AddStep("export again", () => + { + secondExport = new MemoryStream(); + + new LegacyBeatmapExporter(LocalStorage) + .ExportToStream((BeatmapSetInfo)beatmap.BeatmapInfo.BeatmapSet!, secondExport, null); + }); + + const string osu_filename = @"legacy export - stability test (spaceman_atlas) [].osu"; + + AddAssert("exports are identical", + () => getStringContentsOf(osu_filename, firstExport.GetBuffer()), + () => Is.EqualTo(getStringContentsOf(osu_filename, secondExport.GetBuffer()))); + + string getStringContentsOf(string filename, byte[] archiveBytes) + { + using var memoryStream = new MemoryStream(archiveBytes); + using var archiveReader = new ZipArchiveReader(memoryStream); + byte[] fileContent = archiveReader.GetStream(filename).ReadAllBytesToArray(); + return Encoding.UTF8.GetString(fileContent); + } + } + private IWorkingBeatmap importBeatmapFromStream(Stream stream) { var imported = beatmaps.Import(new ImportTask(stream, "filename.osz")).GetResultSafely(); diff --git a/osu.Game.Tests/Resources/Archives/legacy-export-stability-test.olz b/osu.Game.Tests/Resources/Archives/legacy-export-stability-test.olz new file mode 100644 index 0000000000000000000000000000000000000000..c6cf33acaf8587cbb040413f5c39d9cc7544d913 GIT binary patch literal 946 zcmWIWW@Zs#-~hsd86}YnP+-Brz+lFpz>t%go|s&zkXliYUsR%?t595$n3S25SyHJ` zl3HA%pix|qn4FrMm=~W|l9O1hsSq8jmtR~O8p6xKzUqQ)It-Uqa5FHnykKTv028T! z(RsHG1pYkNEMaJU@O$BYn=X}xyAq|hn0R}drp;*S)sYl=e5Ez-TEE=N@E4cXB`;xJ zD7tmCwe{v-e?N!Y+lYT(`1NS5e7K&d^eG;v^6C3NU);)F^6O4Z-J{szyP>R(n_rk8 z+<&KS>iRC>u5-Vi>zV%LZV)+pH|y@No&KR8?KW@Cm>2Wcz2@`TCrogcSbRoy=uEWiEqy`#@g*!aY*l22cITd(n3v$tLHi+8cY z3#uCR4o!#&|NZmU)rHk39?UF%sg!f*!tqrCFO1sK{#&1YYKwZk z^B5zt7IM6pwvab7TE%`z?YCs{S94qI4_uy+P!qc4-@&W557ozi|2U!cZ6)`-=J!)t zc@=J~kNJLe={4RLllz@tBraX>Qt8h9_Fp@FZ?o>273(6tYj0uaIp;Nrcdf03?&f^r z&+h+fS-C6j_`R$B?b9l`x%+|}4)5ZXa|_n_mzIBc_T0Rh>20ic68txuiIgw9Kc%p7 zsn*tQGYpy5*FIUQk?HB>Ba;3&=Sfro^OHS~c3XZ}?UrhAJ<6%=@Vm~Z0j(=HPU`9s zZfg~2Yc%%K&8fd5Df7@$>MwJEH#5M?w0;DB?8Hj-a0N_@bl>h($ literal 0 HcmV?d00001 From 1ef02fec062a85737589f9f64f291e094e791ef8 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 20 Nov 2024 12:08:57 -0500 Subject: [PATCH 57/73] Fix "positional hitsounds level" setting not specifying a precision constraint --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index af6fd61a3d..f922981d77 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -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); From 40b95901e3253bf17f668d46df5c46ca8e469b8e Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 20 Nov 2024 12:11:38 -0500 Subject: [PATCH 58/73] Fix more cases of settings with no defined precision values --- osu.Game/Configuration/OsuConfigManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index f922981d77..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); @@ -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); From 945139635f99b827e405273dad54e80f2a606e3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Nov 2024 02:23:42 +0900 Subject: [PATCH 59/73] Add failing test case --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index cf813cfd51..182193714b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -18,6 +18,7 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Mods; @@ -207,7 +208,25 @@ namespace osu.Game.Tests.Visual.Gameplay } [Test] - public void TestBlockLoadViaFocus() + public void TestLoadNotBlockedViaArbitraryFocus() + { + AddStep("load dummy beatmap", () => resetPlayer(false)); + AddUntilStep("wait for current", () => loader.IsCurrentScreen()); + + AddUntilStep("click settings slider", () => + { + InputManager.MoveMouseTo(loader.ChildrenOfType>().First()); + InputManager.Click(MouseButton.Left); + + return InputManager.FocusedDrawable is OsuSliderBar; + }); + + AddUntilStep("wait for load ready", () => player?.LoadState == LoadState.Ready); + AddUntilStep("loads", () => !loader.IsCurrentScreen()); + } + + [Test] + public void TestBlockLoadViaOverlayFocus() { AddStep("load dummy beatmap", () => resetPlayer(false)); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); From ae98f63b511870c11aad3d4955a1a5454a0aad58 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Nov 2024 02:23:56 +0900 Subject: [PATCH 60/73] Fix beatmap load not continuing when when settings slider is focused Regressed with recent sliderbar focus changes. Closes #30716. --- osu.Game/Screens/Play/PlayerLoader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index aedc268d70..3e36c630db 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -122,7 +122,9 @@ namespace osu.Game.Screens.Play // not ready if the user is dragging a slider or otherwise. && (inputManager.DraggedDrawable == null || inputManager.DraggedDrawable is OsuLogo) // not ready if a focused overlay is visible, like settings. - && inputManager.FocusedDrawable == null; + && inputManager.FocusedDrawable is not OsuFocusedOverlayContainer + // or if a child of a focused overlay is focused, like settings' search textbox. + && inputManager.FocusedDrawable?.FindClosestParent() == null; private readonly Func createPlayer; From 37394a5027db31440aa06f208d7563771f79b34d Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 21 Nov 2024 14:04:42 +1300 Subject: [PATCH 61/73] Use consistent decimal places in BeatmapAttributeText --- osu.Game/Skinning/Components/BeatmapAttributeText.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/Components/BeatmapAttributeText.cs b/osu.Game/Skinning/Components/BeatmapAttributeText.cs index 79a1ed4d7c..f1c27434fa 100644 --- a/osu.Game/Skinning/Components/BeatmapAttributeText.cs +++ b/osu.Game/Skinning/Components/BeatmapAttributeText.cs @@ -211,19 +211,19 @@ namespace osu.Game.Skinning.Components return beatmap.Value.BeatmapInfo.Status.GetLocalisableDescription(); case BeatmapAttribute.BPM: - return FormatUtils.RoundBPM(beatmap.Value.BeatmapInfo.BPM, ModUtils.CalculateRateWithMods(mods.Value)).ToLocalisableString(@"F2"); + return FormatUtils.RoundBPM(beatmap.Value.BeatmapInfo.BPM, ModUtils.CalculateRateWithMods(mods.Value)).ToLocalisableString(@"0.##"); case BeatmapAttribute.CircleSize: - return computeDifficulty().CircleSize.ToLocalisableString(@"F2"); + return computeDifficulty().CircleSize.ToLocalisableString(@"0.##"); case BeatmapAttribute.HPDrain: - return computeDifficulty().DrainRate.ToLocalisableString(@"F2"); + return computeDifficulty().DrainRate.ToLocalisableString(@"0.##"); case BeatmapAttribute.Accuracy: - return computeDifficulty().OverallDifficulty.ToLocalisableString(@"F2"); + return computeDifficulty().OverallDifficulty.ToLocalisableString(@"0.##"); case BeatmapAttribute.ApproachRate: - return computeDifficulty().ApproachRate.ToLocalisableString(@"F2"); + return computeDifficulty().ApproachRate.ToLocalisableString(@"0.##"); case BeatmapAttribute.StarRating: return (starDifficulty?.Stars ?? 0).ToLocalisableString(@"F2"); From fe8e9d455a6a91f7e03a3be0c4331c6ab53849f0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 18:04:44 +0900 Subject: [PATCH 62/73] Add failing test --- .../TestScenePlaylistsRoomSubScreen.cs | 41 ++++++++++++++++++ .../Visual/OnlinePlay/TestRoomManager.cs | 42 +++++++------------ 2 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs new file mode 100644 index 0000000000..4ff8123fbb --- /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 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/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index e813b49844..b1e3eafacc 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -29,38 +29,28 @@ namespace osu.Game.Tests.Visual.OnlinePlay { for (int i = 0; i < count; i++) { - var room = new Room + AddRoom(new Room { - RoomID = -currentRoomId, Name = $@"Room {currentRoomId}", Host = new APIUser { Username = @"Host" }, Duration = TimeSpan.FromSeconds(10), Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal, - }; - - if (withPassword) - room.Password = @"password"; - - if (ruleset != null) - { - room.PlaylistItemStats = new Room.RoomPlaylistItemStats - { - RulesetIDs = new[] { ruleset.OnlineID }, - }; - - room.Playlist = - [ - new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) - { - RulesetID = ruleset.OnlineID, - } - ]; - } - - CreateRoom(room); - - currentRoomId++; + Password = withPassword ? @"password" : null, + PlaylistItemStats = ruleset == null + ? null + : new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] }, + Playlist = ruleset == null + ? Array.Empty() + : [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }] + }); } } + + public void AddRoom(Room room) + { + room.RoomID = -currentRoomId; + CreateRoom(room); + currentRoomId++; + } } } From 7018672275fd5621d1ceb4ecf9a4411bbdc91fd7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 18:11:41 +0900 Subject: [PATCH 63/73] Fix playlist room status resetting on enter --- osu.Game/Online/Rooms/GetRoomsRequest.cs | 21 --------------------- osu.Game/Online/Rooms/Room.cs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index cd797a9668..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 != null && DateTimeOffset.Now >= room.EndDate) - room.Status = new RoomStatusEnded(); - else if (room.HasPassword) - room.Status = new RoomStatusOpenPrivate(); - else - room.Status = new RoomStatusOpen(); - } - } - } - protected override string Target => "rooms"; } } diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index a26a3fcb34..486f70c0ed 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using Newtonsoft.Json; using osu.Game.IO.Serialization.Converters; using osu.Game.Online.API.Requests.Responses; @@ -264,6 +265,18 @@ namespace osu.Game.Online.Rooms 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; From 8dbf750446c2ccb23079b1923acffe944ee061fd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 18:38:56 +0900 Subject: [PATCH 64/73] Fix inspection --- .../Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 4ff8123fbb..4306fc1e6a 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -12,7 +12,7 @@ using osu.Game.Tests.Visual.OnlinePlay; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene + public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene { protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; From 2138729c02fda9b619a3298a7ec3b63628141073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Nov 2024 12:17:43 +0100 Subject: [PATCH 65/73] Do not show distance to next/previous object if said object is a banana shower The results of such a display were a little bit nonsensical. --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs index c279354d9a..bfabcbdb83 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectInspector.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -21,14 +22,14 @@ namespace osu.Game.Rulesets.Catch.Edit HitObject? precedingObject = EditorBeatmap.HitObjects.LastOrDefault(ho => ho.GetEndTime() < firstSelectedHitObject.StartTime); HitObject? nextObject = EditorBeatmap.HitObjects.FirstOrDefault(ho => ho.StartTime > lastSelectedHitObject.GetEndTime()); - if (precedingObject != null) + if (precedingObject != null && precedingObject is not BananaShower) { double previousSnap = snapProvider.ReadCurrentDistanceSnap(precedingObject, firstSelectedHitObject); AddHeader("To previous"); AddValue($"{previousSnap:#,0.##}x"); } - if (nextObject != null) + if (nextObject != null && nextObject is not BananaShower) { double nextSnap = snapProvider.ReadCurrentDistanceSnap(lastSelectedHitObject, nextObject); AddHeader("To next"); From 6870c99eb2f1429500a68258daa647a3bb44f9db Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 20:26:44 +0900 Subject: [PATCH 66/73] Enable NRT for multiplayer and playlists --- .../StatefulMultiplayerClientTest.cs | 2 +- .../OnlinePlay/TestSceneCatchUpSyncManager.cs | 12 +- ...MultiplayerGameplayLeaderboardTestScene.cs | 10 +- .../TestSceneCreateMultiplayerMatchButton.cs | 6 +- .../TestSceneDrawableRoomParticipantsList.cs | 10 +- .../TestSceneDrawableRoomPlaylist.cs | 13 +- .../TestSceneFreeModSelectOverlay.cs | 11 +- .../TestSceneGameplayChatDisplay.cs | 4 +- .../Multiplayer/TestSceneHostOnlyQueueMode.cs | 12 +- .../TestSceneLoungeRoomsContainer.cs | 12 +- .../TestSceneMatchBeatmapDetailArea.cs | 2 +- .../TestSceneMultiSpectatorLeaderboard.cs | 10 +- .../TestSceneMultiSpectatorScreen.cs | 2 +- ...TestSceneMultiplayerGameplayLeaderboard.cs | 4 +- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 2 +- .../TestSceneMultiplayerLoungeSubScreen.cs | 23 +-- .../TestSceneMultiplayerMatchSongSelect.cs | 30 ++- .../TestSceneMultiplayerMatchSubScreen.cs | 33 ++- .../TestSceneMultiplayerPlaylist.cs | 2 +- .../TestSceneMultiplayerQueueList.cs | 2 +- .../TestSceneMultiplayerResults.cs | 4 +- .../TestSceneMultiplayerSpectateButton.cs | 2 +- ...TestSceneMultiplayerSpectatorPlayerGrid.cs | 4 +- .../TestSceneMultiplayerTeamResults.cs | 4 +- .../TestScenePlaylistsRoomSettingsPlaylist.cs | 10 +- .../TestScenePlaylistsSongSelect.cs | 29 ++- .../TestSceneStarRatingRangeDisplay.cs | 2 +- .../TestScenePlaylistsLoungeSubScreen.cs | 6 +- .../TestScenePlaylistsMatchSettingsOverlay.cs | 26 +-- .../TestScenePlaylistsParticipantsList.cs | 4 +- .../TestScenePlaylistsResultsScreen.cs | 15 +- .../TestScenePlaylistsRoomCreation.cs | 8 +- .../Components/ListingPollingComponent.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylist.cs | 12 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 192 ++++++++++-------- .../Lounge/Components/FilterCriteria.cs | 8 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 49 +++-- .../Match/Components/MatchLeaderboardScore.cs | 4 +- .../Match/Components/MatchTypePicker.cs | 9 +- .../Components/RoomAvailabilityPicker.cs | 4 +- .../OnlinePlay/Match/RoomModSelectOverlay.cs | 2 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 57 +++--- .../OnlinePlay/Match/UserModSelectButton.cs | 1 - .../CreateMultiplayerMatchButton.cs | 10 +- .../Multiplayer/GameplayChatDisplay.cs | 6 +- .../Match/MultiplayerCountdownButton.cs | 11 +- .../Match/MultiplayerReadyButton.cs | 28 +-- .../Multiplayer/MultiplayerLoungeSubScreen.cs | 12 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 +- .../Multiplayer/MultiplayerPlayerLoader.cs | 8 +- .../OnlinePlay/OngoingOperationTracker.cs | 4 +- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 12 +- .../Playlists/PlaylistsRoomSubScreen.cs | 9 +- .../IOnlinePlayTestSceneDependencies.cs | 2 +- .../Visual/OnlinePlay/OnlinePlayTestScene.cs | 2 +- .../OnlinePlayTestSceneDependencies.cs | 4 +- 56 files changed, 357 insertions(+), 411 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 2e9170019c..559db16751 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer AddStep("create room initially in gameplay", () => { var newRoom = new Room(); - newRoom.CopyFrom(SelectedRoom.Value); + newRoom.CopyFrom(SelectedRoom.Value!); newRoom.RoomID = null; MultiplayerClient.RoomSetupAction = room => diff --git a/osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.cs b/osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.cs index 7b0b211899..8f6325c70b 100644 --- a/osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.cs +++ b/osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.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 NUnit.Framework; @@ -21,12 +19,12 @@ namespace osu.Game.Tests.OnlinePlay [HeadlessTest] public partial class TestSceneCatchUpSyncManager : OsuTestScene { - private GameplayClockContainer master; - private SpectatorSyncManager syncManager; + private GameplayClockContainer master = null!; + private SpectatorSyncManager syncManager = null!; - private Dictionary clocksById; - private SpectatorPlayerClock player1; - private SpectatorPlayerClock player2; + private Dictionary clocksById = null!; + private SpectatorPlayerClock player1 = null!; + private SpectatorPlayerClock player2 = null!; [SetUp] public void Setup() diff --git a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs index 906eea9553..1eb08ad3c8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/MultiplayerGameplayLeaderboardTestScene.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; @@ -31,7 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer protected readonly BindableList MultiplayerUsers = new BindableList(); - protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; } + protected MultiplayerGameplayLeaderboard? Leaderboard { get; private set; } protected virtual MultiplayerRoomUser CreateUser(int userId) => new MultiplayerRoomUser(userId); @@ -40,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private readonly BindableList multiplayerUserIds = new BindableList(); private readonly BindableDictionary watchedUserStates = new BindableDictionary(); - private OsuConfigManager config; + private OsuConfigManager config = null!; private readonly Mock spectatorClient = new Mock(); private readonly Mock multiplayerClient = new Mock(); @@ -133,7 +131,7 @@ namespace osu.Game.Tests.Visual.Multiplayer LoadComponentAsync(Leaderboard = CreateLeaderboard(), Add); }); - AddUntilStep("wait for load", () => Leaderboard.IsLoaded); + AddUntilStep("wait for load", () => Leaderboard!.IsLoaded); AddStep("check watch requests were sent", () => { @@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestScoreUpdates() { AddRepeatStep("update state", UpdateUserStatesRandomly, 100); - AddToggleStep("switch compact mode", expanded => Leaderboard.Expanded.Value = expanded); + AddToggleStep("switch compact mode", expanded => Leaderboard!.Expanded.Value = expanded); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs index 11b0f8b91c..6d6d30d517 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.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.Graphics; @@ -12,7 +10,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene { - private CreateMultiplayerMatchButton button; + private CreateMultiplayerMatchButton button = null!; public override void SetUpSteps() { @@ -29,7 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestButtonEnableStateChanges() { - IDisposable joiningRoomOperation = null; + IDisposable joiningRoomOperation = null!; assertButtonEnableState(true); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs index b909b934b3..c1662bf944 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomParticipantsList.cs @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4); AddAssert("46 hidden users", () => list.ChildrenOfType().Single().Count == 46); - AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1)); + AddStep("remove from end", () => removeUserAt(SelectedRoom.Value!.RecentParticipants.Count - 1)); AddAssert("4 circles displayed", () => list.ChildrenOfType().Count() == 4); AddAssert("45 hidden users", () => list.ChildrenOfType().Single().Count == 45); @@ -138,18 +138,18 @@ namespace osu.Game.Tests.Visual.Multiplayer private void addUser(int id) { - SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Append(new APIUser + SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Append(new APIUser { Id = id, Username = $"User {id}" }).ToArray(); - SelectedRoom.Value.ParticipantCount++; + SelectedRoom.Value!.ParticipantCount++; } private void removeUserAt(int index) { - SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value.RecentParticipants[index])).ToArray(); - SelectedRoom.Value.ParticipantCount--; + SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value!.RecentParticipants[index])).ToArray(); + SelectedRoom.Value!.ParticipantCount--; } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 2ef56bd54e..18cd720bf2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.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.Diagnostics; @@ -39,9 +37,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneDrawableRoomPlaylist : MultiplayerTestScene { - private TestPlaylist playlist; - - private BeatmapManager manager; + private TestPlaylist playlist = null!; + private BeatmapManager manager = null!; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -199,14 +196,14 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestDownloadButtonHiddenWhenBeatmapExists() { - Live imported = null; + Live imported = null!; AddStep("import beatmap", () => { var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; Debug.Assert(beatmap.BeatmapSet != null); - imported = manager.Import(beatmap.BeatmapSet); + imported = manager.Import(beatmap.BeatmapSet)!; }); createPlaylistWithBeatmaps(() => imported.PerformRead(s => s.Beatmaps.Detach())); @@ -378,7 +375,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - private void createPlaylist(Action setupPlaylist = null) + private void createPlaylist(Action? setupPlaylist = null) { AddStep("create playlist", () => { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 4316653dde..fb54b89a4b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.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; @@ -26,9 +24,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneFreeModSelectOverlay : MultiplayerTestScene { - private FreeModSelectOverlay freeModSelectOverlay; - private FooterButtonFreeMods footerButtonFreeMods; - private ScreenFooter footer; + private FreeModSelectOverlay freeModSelectOverlay = null!; + private FooterButtonFreeMods footerButtonFreeMods = null!; + private ScreenFooter footer = null!; private readonly Bindable>> availableMods = new Bindable>>(); [BackgroundDependencyLoader] @@ -49,8 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddToggleStep("toggle visibility", visible => { - if (freeModSelectOverlay != null) - freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden; + freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden; }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index 6a500bbe55..235d142820 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.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 Moq; using NUnit.Framework; @@ -20,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneGameplayChatDisplay : OsuManualInputManagerTestScene { - private GameplayChatDisplay chatDisplay; + private GameplayChatDisplay chatDisplay = null!; [Cached(typeof(ILocalUserPlayInfo))] private ILocalUserPlayInfo localUserInfo; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index e0364fd65d..55c9e8142f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.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; @@ -70,13 +68,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { RunGameplay(); - IBeatmapInfo firstBeatmap = null; - AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap); + IBeatmapInfo firstBeatmap = null!; + AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom!.Playlist[0].Beatmap); selectNewItem(() => OtherBeatmap); - AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap == firstBeatmap); - AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap); + AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom!.Playlist[0].Beatmap == firstBeatmap); + AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom!.Playlist[1].Beatmap != firstBeatmap); } [Test] @@ -103,7 +101,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded); - BeatmapInfo otherBeatmap = null; + BeatmapInfo otherBeatmap = null!; AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap())); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index e28790f2ed..797b69ec72 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.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; @@ -23,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; - private RoomsContainer container; + private RoomsContainer container = null!; public override void SetUpSteps() { @@ -65,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select first room", () => container.Rooms.First().TriggerClick()); AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight))); - AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID))); + AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)!)); AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight))); AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight))); @@ -157,7 +155,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo)); // Todo: What even is this case...? - AddStep("set empty filter criteria", () => container.Filter.Value = null); + AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria()); AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5); AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }); @@ -195,9 +193,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true)); } - private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; + private bool checkRoomSelected(Room? room) => SelectedRoom.Value == room; - private Room getRoomInFlow(int index) => + private Room? getRoomInFlow(int index) => (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index dd39db49c5..813a420cbd 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createNewItem() { - SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) + SelectedRoom.Value!.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { ID = SelectedRoom.Value.Playlist.Count, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index 4bf2ebc1a4..3245b3c6a9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.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 System.Linq; using NUnit.Framework; @@ -18,8 +16,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiSpectatorLeaderboard : MultiplayerTestScene { - private Dictionary clocks; - private MultiSpectatorLeaderboard leaderboard; + private Dictionary clocks = null!; + private MultiSpectatorLeaderboard? leaderboard; [SetUpSteps] public override void SetUpSteps() @@ -55,13 +53,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }, Add); }); - AddUntilStep("wait for load", () => leaderboard.IsLoaded); + AddUntilStep("wait for load", () => leaderboard!.IsLoaded); AddUntilStep("wait for user population", () => leaderboard.ChildrenOfType().Count() == 2); AddStep("add clock sources", () => { foreach ((int userId, var clock) in clocks) - leaderboard.AddClock(userId, clock); + leaderboard!.AddClock(userId, clock); }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 2b17f91e68..85352ada3c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -455,7 +455,7 @@ namespace osu.Game.Tests.Visual.Multiplayer applyToBeatmap?.Invoke(Beatmap.Value); - LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray())); + LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value!, playingUsers.ToArray())); }); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded)); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index bafe373d57..2f232a6164 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -37,10 +37,10 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestPerUserMods() { - AddStep("first user has no mods", () => Assert.That(((TestLeaderboard)Leaderboard).UserMods[0], Is.Empty)); + AddStep("first user has no mods", () => Assert.That(((TestLeaderboard)Leaderboard!).UserMods[0], Is.Empty)); AddStep("last user has NF mod", () => { - Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1], Has.One.Items); + Assert.That(((TestLeaderboard)Leaderboard!).UserMods[TOTAL_USERS - 1], Has.One.Items); Assert.That(((TestLeaderboard)Leaderboard).UserMods[TOTAL_USERS - 1].Single(), Is.TypeOf()); }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 37662ffce8..3f1db308c0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { LoadComponentAsync(new MatchScoreDisplay { - Team1Score = { BindTarget = Leaderboard.TeamScores[0] }, + Team1Score = { BindTarget = Leaderboard!.TeamScores[0] }, Team2Score = { BindTarget = Leaderboard.TeamScores[1] } }, Add); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index cf25e06799..9951f62c77 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.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.Containers; @@ -22,10 +20,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; - private LoungeSubScreen loungeScreen; - - private Room lastJoinedRoom; - private string lastJoinedPassword; + private LoungeSubScreen loungeScreen = null!; + private Room? lastJoinedRoom; + private string? lastJoinedPassword; public override void SetUpSteps() { @@ -87,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestJoinRoomWithIncorrectPasswordViaButton() { - DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); @@ -97,14 +94,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); AddAssert("room not joined", () => loungeScreen.IsCurrentScreen()); - AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible); + AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible); AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox); } [Test] public void TestJoinRoomWithIncorrectPasswordViaEnter() { - DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); @@ -114,14 +111,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press enter", () => InputManager.Key(Key.Enter)); AddAssert("room not joined", () => loungeScreen.IsCurrentScreen()); - AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible); + AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible); AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox); } [Test] public void TestJoinRoomWithCorrectPassword() { - DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); @@ -137,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestJoinRoomWithPasswordViaKeyboardOnly() { - DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; + DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null; AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("select room", () => InputManager.Key(Key.Down)); @@ -150,7 +147,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("room join password correct", () => lastJoinedPassword == "password"); } - private void onRoomJoined(Room room, string password) + private void onRoomJoined(Room room, string? password) { lastJoinedRoom = room; lastJoinedPassword = password; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index bd635b1669..2a5f16d091 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.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.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -35,17 +32,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayerMatchSongSelect : MultiplayerTestScene { - private BeatmapManager manager; - private RulesetStore rulesets; + private BeatmapManager manager = null!; + private RulesetStore rulesets = null!; - private IList beatmaps => importedBeatmapSet?.PerformRead(s => s.Beatmaps) ?? new List(); + private IList beatmaps => importedBeatmapSet.PerformRead(s => s.Beatmaps); - private TestMultiplayerMatchSongSelect songSelect; - - private Live importedBeatmapSet; + private TestMultiplayerMatchSongSelect songSelect = null!; + private Live importedBeatmapSet = null!; [Resolved] - private OsuConfigManager configManager { get; set; } + private OsuConfigManager configManager { get; set; } = null!; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -57,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(detachedBeatmapStore = new DetachedBeatmapStore()); Dependencies.Cache(Realm); - importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray())); + importedBeatmapSet = manager.Import(TestResources.CreateTestBeatmapSetInfo(8, rulesets.AvailableRulesets.ToArray()))!; Add(detachedBeatmapStore); } @@ -71,7 +67,7 @@ namespace osu.Game.Tests.Visual.Multiplayer SelectedMods.SetDefault(); }); - AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value))); + AddStep("create song select", () => LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value!))); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded); } @@ -88,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestBeatmapConfirmed() { - BeatmapInfo selectedBeatmap = null; + BeatmapInfo selectedBeatmap = null!; setUp(); @@ -117,8 +113,8 @@ namespace osu.Game.Tests.Visual.Multiplayer setUp(); AddStep("change ruleset", () => Ruleset.Value = new OsuRuleset().RulesetInfo); - AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod) }); - AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod) }); + AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod)! }); + AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod)! }); AddAssert("freemods empty", () => songSelect.FreeMods.Value.Count == 0); @@ -141,7 +137,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create song select", () => { - SelectedRoom.Value.Playlist.Single().RulesetID = 2; + SelectedRoom.Value!.Playlist.Single().RulesetID = 2; songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value, SelectedRoom.Value.Playlist.Single()); songSelect.OnLoadComplete += _ => Ruleset.Value = new TaikoRuleset().RulesetInfo; LoadScreen(songSelect); @@ -171,7 +167,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public new BeatmapCarousel Carousel => base.Carousel; - public TestMultiplayerMatchSongSelect(Room room, [CanBeNull] PlaylistItem itemToEdit = null) + public TestMultiplayerMatchSongSelect(Room room, PlaylistItem? itemToEdit = null) : base(room, itemToEdit) { } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index abcb591cf6..8ea52f8099 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -1,10 +1,7 @@ // 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 JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -42,10 +39,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { public partial class TestSceneMultiplayerMatchSubScreen : MultiplayerTestScene { - private MultiplayerMatchSubScreen screen; - - private BeatmapManager beatmaps; - private BeatmapSetInfo importedSet; + private MultiplayerMatchSubScreen screen = null!; + private BeatmapManager beatmaps = null!; + private BeatmapSetInfo importedSet = null!; public TestSceneMultiplayerMatchSubScreen() : base(false) @@ -70,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("load match", () => { SelectedRoom.Value = new Room { Name = "Test Room" }; - LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value)); + LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value!)); }); AddUntilStep("wait for load", () => screen.IsCurrentScreen()); @@ -81,7 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -100,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo) { @@ -125,7 +121,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set playlist", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -142,7 +138,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("set playlist", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo) { @@ -173,7 +169,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item with allowed mod", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -202,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item with allowed mod", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -226,7 +222,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item with no allowed mods", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -249,7 +245,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add two playlist items", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo) { @@ -285,7 +281,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { @@ -320,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/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index 518ea2b511..36f5bba384 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create list", () => { - Child = list = new MultiplayerPlaylist(SelectedRoom.Value) + Child = list = new MultiplayerPlaylist(SelectedRoom.Value!) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index 50f55ebde0..3ef2e4ecf4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create playlist", () => { - Child = playlist = new MultiplayerQueueList(SelectedRoom.Value) + Child = playlist = new MultiplayerQueueList(SelectedRoom.Value!) { Anchor = Anchor.Centre, Origin = Anchor.Centre, 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 b4f55bb6a3..1429f86164 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("create button", () => { - PlaylistItem item = SelectedRoom.Value.Playlist.First(); + PlaylistItem item = SelectedRoom.Value!.Playlist.First(); AvailabilityTracker.SelectedItem.Value = 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 b55102518a..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,14 +65,14 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestItemAddedIfEmptyOnStart() { AddStep("finalise selection", () => songSelect.FinaliseSelection()); - AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1); + AddAssert("playlist has 1 item", () => 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); + AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1); } [Test] @@ -83,7 +80,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("finalise selection", () => songSelect.FinaliseSelection()); - AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1); + AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1); } [Test] @@ -91,7 +88,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); - AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2); + AddAssert("playlist has 2 items", () => SelectedRoom.Value!.Playlist.Count == 2); } [Test] @@ -99,10 +96,10 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!()); - AddStep("rearrange", () => SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Skip(1).Append(SelectedRoom.Value.Playlist[0]).ToArray()); + AddStep("rearrange", () => 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); + AddAssert("new item has id 2", () => SelectedRoom.Value!.Playlist.Last().ID == 2); } /// @@ -118,13 +115,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("item 1 has rate 1.5", () => { - var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset()); + var mod = (OsuModDoubleTime)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); }); } @@ -135,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,7 +147,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2); AddAssert("item has rate 1.5", () => { - var m = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset()); + var m = (OsuModDoubleTime)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/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs index 195b40bd13..88afef7de2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("set playlist", () => { - SelectedRoom.Value.Playlist = + SelectedRoom.Value!.Playlist = [ new PlaylistItem(new BeatmapInfo { StarRating = min }), new PlaylistItem(new BeatmapInfo { StarRating = max }), 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 321f563140..5868331451 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -32,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 } @@ -45,19 +45,19 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("clear name and beatmap", () => { - SelectedRoom.Value.Name = ""; - SelectedRoom.Value.Playlist = []; + SelectedRoom.Value!.Name = ""; + SelectedRoom.Value!.Playlist = []; }); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); - AddStep("set name", () => SelectedRoom.Value.Name = "Room name"); + AddStep("set name", () => SelectedRoom.Value!.Name = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); - AddStep("set beatmap", () => SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]); + AddStep("set beatmap", () => SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]); AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value); - AddStep("clear name", () => SelectedRoom.Value.Name = ""); + AddStep("clear name", () => SelectedRoom.Value!.Name = ""); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); } @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Playlists { settings.NameField.Current.Value = expected_name; settings.DurationField.Current.Value = expectedDuration; - SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]; + SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]; RoomManager.CreateRequested = r => { @@ -98,8 +98,8 @@ namespace osu.Game.Tests.Visual.Playlists { var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo; - SelectedRoom.Value.Name = "Test Room"; - SelectedRoom.Value.Playlist = [new PlaylistItem(beatmap)]; + SelectedRoom.Value!.Name = "Test Room"; + SelectedRoom.Value!.Playlist = [new PlaylistItem(beatmap)]; errorMessage = $"{not_found_prefix} {beatmap.OnlineID}"; @@ -107,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] @@ -125,8 +125,8 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("setup", () => { - SelectedRoom.Value.Name = "Test Room"; - SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]; + SelectedRoom.Value!.Name = "Test Room"; + SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]; RoomManager.CreateRequested = _ => failText; }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs index f72a2cd655..c60b208ffc 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsParticipantsList.cs @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("create component", () => { - Child = new ParticipantsDisplay(SelectedRoom.Value, 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(SelectedRoom.Value, 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 6a53019b76..0270840597 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Playlists 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()); } @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Playlists ]; }); - AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]); + AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value!.Playlist[0]); } [Test] @@ -199,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", () => { @@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.Playlists private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { - public new Bindable SelectedItem => base.SelectedItem; + public new Bindable SelectedItem => base.SelectedItem; public new Bindable Beatmap => base.Beatmap; diff --git a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs index 67c3586a35..b10ce8ed1b 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ListingPollingComponent.cs @@ -18,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() 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/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/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 90288a1067..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() { @@ -262,7 +259,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge 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; @@ -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/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/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 4461cf5402..0d9c210149 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#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; @@ -37,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached(typeof(IPreviewTrackOwner))] public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner { - public readonly Bindable SelectedItem = new Bindable(); + public readonly Bindable SelectedItem = new Bindable(); public override bool? ApplyModTrackAdjustments => true; @@ -52,9 +49,9 @@ namespace osu.Game.Screens.OnlinePlay.Match /// A container that provides controls for selection of user mods. /// This will be shown/hidden automatically when applicable. /// - protected Drawable UserModsSection; + protected Drawable? UserModsSection; - private Sample sampleStart; + private Sample? sampleStart; /// /// Any mods applied by/to the local user. @@ -62,28 +59,28 @@ namespace osu.Game.Screens.OnlinePlay.Match protected readonly Bindable> UserMods = new Bindable>(Array.Empty()); [Resolved(CanBeNull = true)] - private IOverlayManager overlayManager { get; set; } + private IOverlayManager? overlayManager { get; set; } [Resolved] - private MusicController music { get; set; } + private MusicController music { get; set; } = null!; [Resolved] - private BeatmapManager beatmapManager { get; set; } + private BeatmapManager beatmapManager { get; set; } = null!; [Resolved] - protected RulesetStore Rulesets { get; private set; } + protected RulesetStore Rulesets { get; private set; } = null!; [Resolved] private IAPIProvider api { get; set; } = null!; [Resolved(canBeNull: true)] - protected OnlinePlayScreen ParentScreen { get; private set; } + protected OnlinePlayScreen? ParentScreen { get; private set; } [Resolved] private PreviewTrackManager previewTrackManager { get; set; } = null!; [Resolved(canBeNull: true)] - private IDialogOverlay dialogOverlay { get; set; } + private IDialogOverlay? dialogOverlay { get; set; } [Cached] private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); @@ -93,13 +90,11 @@ namespace osu.Game.Screens.OnlinePlay.Match public readonly Room Room; private readonly bool allowEdit; - internal ModSelectOverlay UserModsSelectOverlay { get; private set; } + internal ModSelectOverlay UserModsSelectOverlay { get; private set; } = null!; - [CanBeNull] - private IDisposable userModsSelectOverlayRegistration; - - private RoomSettingsOverlay settingsOverlay; - private Drawable mainContent; + private IDisposable? userModsSelectOverlayRegistration; + private RoomSettingsOverlay settingsOverlay = null!; + private Drawable mainContent = null!; /// /// Creates a new . @@ -265,7 +260,7 @@ namespace osu.Game.Screens.OnlinePlay.Match updateSetupState(); } - private void onRoomPropertyChanged(object sender, PropertyChangedEventArgs e) + private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Room.RoomID)) updateSetupState(); @@ -388,6 +383,9 @@ namespace osu.Game.Screens.OnlinePlay.Match protected void StartPlay() { + if (SelectedItem.Value == null) + return; + // User may be at song select or otherwise when the host starts gameplay. // Ensure that they first return to this screen, else global bindables (beatmap etc.) may be in a bad state. if (!this.IsCurrentScreen()) @@ -401,29 +399,28 @@ namespace osu.Game.Screens.OnlinePlay.Match sampleStart?.Play(); // fallback is to allow this class to operate when there is no parent OnlineScreen (testing purposes). - var targetScreen = (Screen)ParentScreen ?? this; + var targetScreen = (Screen?)ParentScreen ?? this; - targetScreen.Push(CreateGameplayScreen()); + targetScreen.Push(CreateGameplayScreen(SelectedItem.Value)); } /// /// Creates the gameplay screen to be entered. /// + /// /// The screen to enter. - protected abstract Screen CreateGameplayScreen(); + protected abstract Screen CreateGameplayScreen(PlaylistItem selectedItem); private void selectedItemChanged() { updateWorkingBeatmap(); - var selected = SelectedItem.Value; - - if (selected == null) + if (SelectedItem.Value is not PlaylistItem selected) return; - var rulesetInstance = Rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); + var rulesetInstance = Rulesets.GetRuleset(selected.RulesetID)?.CreateInstance(); Debug.Assert(rulesetInstance != null); - var allowedMods = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance)); + var allowedMods = selected.AllowedMods.Select(m => m.ToMod(rulesetInstance)); // Remove any user mods that are no longer allowed. UserMods.Value = UserMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToList(); @@ -494,7 +491,7 @@ namespace osu.Game.Screens.OnlinePlay.Match cancelTrackLooping(); } - private void applyLoopingToTrack(ValueChangedEvent _ = null) + private void applyLoopingToTrack(ValueChangedEvent? _ = null) { if (!this.IsCurrentScreen()) return; @@ -503,8 +500,8 @@ namespace osu.Game.Screens.OnlinePlay.Match if (track != null) { - Beatmap.Value.PrepareTrackForPreview(true); - music?.EnsurePlayingSomething(); + Beatmap.Value!.PrepareTrackForPreview(true); + music.EnsurePlayingSomething(); } } diff --git a/osu.Game/Screens/OnlinePlay/Match/UserModSelectButton.cs b/osu.Game/Screens/OnlinePlay/Match/UserModSelectButton.cs index f3ea82be99..219bb6a2e3 100644 --- a/osu.Game/Screens/OnlinePlay/Match/UserModSelectButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/UserModSelectButton.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 7975597beb..9de32267a2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.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.Bindables; using osu.Game.Online.Multiplayer; @@ -12,14 +10,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public partial class CreateMultiplayerMatchButton : CreateRoomButton { - private IBindable isConnected; - private IBindable operationInProgress; + private IBindable isConnected = null!; + private IBindable operationInProgress = null!; [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; [Resolved] - private OngoingOperationTracker ongoingOperationTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index d4483044e0..9a03a131b4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -1,9 +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 JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -20,8 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public partial class GameplayChatDisplay : MatchChatDisplay, IKeyBindingHandler { [Resolved(CanBeNull = true)] - [CanBeNull] - private ILocalUserPlayInfo localUserInfo { get; set; } + private ILocalUserPlayInfo? localUserInfo { get; set; } private readonly IBindable localUserPlaying = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs index e1543eaceb..50e996d266 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerCountdownButton.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 Humanizer; @@ -33,15 +31,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match TimeSpan.FromMinutes(2) }; - public new Action Action; - - public Action CancelAction; + public new required Action Action; + public required Action CancelAction; [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; private readonly Drawable background; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 7ce3dde7c2..ca8bc0b262 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -1,14 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; +using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Graphics; @@ -20,17 +19,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public partial class MultiplayerReadyButton : ReadyButton { [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; - [CanBeNull] - private MultiplayerRoom room => multiplayerClient.Room; + private MultiplayerRoom? room => multiplayerClient.Room; - private Sample countdownTickSample; - private Sample countdownWarnSample; - private Sample countdownWarnFinalSample; + private Sample? countdownTickSample; + private Sample? countdownWarnSample; + private Sample? countdownWarnFinalSample; [BackgroundDependencyLoader] private void load(AudioManager audio) @@ -48,13 +46,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match onRoomUpdated(); } - private MultiplayerCountdown countdown; + private MultiplayerCountdown? countdown; private double countdownChangeTime; - private ScheduledDelegate countdownUpdateDelegate; + private ScheduledDelegate? countdownUpdateDelegate; private void onRoomUpdated() => Scheduler.AddOnce(() => { - MultiplayerCountdown newCountdown = room?.ActiveCountdowns.SingleOrDefault(c => c is MatchStartCountdown); + MultiplayerCountdown? newCountdown = room?.ActiveCountdowns.SingleOrDefault(c => c is MatchStartCountdown); if (newCountdown != countdown) { @@ -171,6 +169,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { get { + Debug.Assert(countdown != null); + double timeElapsed = Time.Current - countdownChangeTime; TimeSpan remaining; @@ -224,7 +224,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.Dispose(isDisposing); - if (multiplayerClient != null) + if (multiplayerClient.IsNotNull()) multiplayerClient.RoomUpdated -= onRoomUpdated; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index a5cc267569..50358ea9d3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.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 System.Linq; using System.Threading.Tasks; @@ -27,12 +25,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public partial class MultiplayerLoungeSubScreen : LoungeSubScreen { [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; [Resolved] - private MultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } = null!; - private Dropdown roomAccessTypeDropdown; + private Dropdown roomAccessTypeDropdown = null!; public override void OnResuming(ScreenTransitionEvent e) { @@ -83,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override void OpenNewRoom(Room room) { - if (client?.IsConnected.Value != true) + if (!client.IsConnected.Value) { Logger.Log("Not currently connected to the multiplayer server.", LoggingTarget.Runtime, LogLevel.Important); return; @@ -95,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private partial class MultiplayerListingPollingComponent : ListingPollingComponent { [Resolved] - private MultiplayerClient client { get; set; } + private MultiplayerClient client { get; set; } = null!; private readonly IBindable isConnected = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 65355f02e5..edc45dbf7c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -401,7 +401,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StartPlay(); } - protected override Screen CreateGameplayScreen() + protected override Screen CreateGameplayScreen(PlaylistItem selectedItem) { Debug.Assert(client.LocalUser != null); Debug.Assert(client.Room != null); @@ -415,7 +415,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return new MultiSpectatorScreen(Room, users.Take(PlayerGrid.MAX_PLAYERS).ToArray()); default: - return new MultiplayerPlayerLoader(() => new MultiplayerPlayer(Room, SelectedItem.Value, users)); + return new MultiplayerPlayerLoader(() => new MultiplayerPlayer(Room, selectedItem, users)); } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs index f682508319..7eb7f6610e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayerLoader.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.Threading.Tasks; using osu.Framework.Allocation; @@ -18,9 +16,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public bool GameplayPassed => player?.GameplayState.HasPassed == true; [Resolved] - private MultiplayerClient multiplayerClient { get; set; } + private MultiplayerClient multiplayerClient { get; set; } = null!; - private Player player; + private Player? player; public MultiplayerPlayerLoader(Func createPlayer) : base(createPlayer) @@ -45,7 +43,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer .ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion); } - private void failAndBail(string message = null) + private void failAndBail(string? message = null) { if (!string.IsNullOrEmpty(message)) Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important); diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 7f73d6655f..0c761dba44 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.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 osu.Framework.Bindables; using osu.Framework.Graphics; @@ -22,7 +20,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable inProgress = new BindableBool(); - private LeasedBindable leasedInProgress; + private LeasedBindable? leasedInProgress; public OngoingOperationTracker() { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 1b7041c9bb..13ffb61d57 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.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.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -27,16 +25,16 @@ namespace osu.Game.Screens.OnlinePlay public IScreen CurrentSubScreen => screenStack.CurrentScreen; - public override bool CursorVisible => (screenStack?.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; + public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true; // this is required due to PlayerLoader eventually being pushed to the main stack // while leases may be taken out by a subscreen. public override bool DisallowExternalBeatmapRulesetChanges => true; - protected LoungeSubScreen Lounge { get; private set; } + protected LoungeSubScreen Lounge { get; private set; } = null!; - private OnlinePlayScreenWaveContainer waves; - private ScreenStack screenStack; + private OnlinePlayScreenWaveContainer waves = null!; + private ScreenStack screenStack = null!; [Cached(Type = typeof(IRoomManager))] protected RoomManager RoomManager { get; private set; } @@ -45,7 +43,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [Resolved] - protected IAPIProvider API { get; private set; } + protected IAPIProvider API { get; private set; } = null!; protected OnlinePlayScreen() { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index f915676fb1..44d1841fb8 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -273,10 +273,13 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})"); } - protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(Room, SelectedItem.Value) + protected override Screen CreateGameplayScreen(PlaylistItem selectedItem) { - Exited = () => leaderboard.RefetchScores() - }); + return new PlayerLoader(() => new PlaylistsPlayer(Room, selectedItem) + { + Exited = () => leaderboard.RefetchScores() + }); + } protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs index 3509519113..8ddc5325db 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/IOnlinePlayTestSceneDependencies.cs @@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// /// The cached . /// - Bindable SelectedRoom { get; } + Bindable SelectedRoom { get; } /// /// The cached diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index eebc3503bc..3f6c175fbd 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlayTestSceneDependencies { - public Bindable SelectedRoom => OnlinePlayDependencies.SelectedRoom; + public Bindable SelectedRoom => OnlinePlayDependencies.SelectedRoom; public IRoomManager RoomManager => OnlinePlayDependencies.RoomManager; public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies.OngoingOperationTracker; public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies.AvailabilityTracker; diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index 06f8cc40b9..e2670c9ad8 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// public class OnlinePlayTestSceneDependencies : IReadOnlyDependencyContainer, IOnlinePlayTestSceneDependencies { - public Bindable SelectedRoom { get; } + public Bindable SelectedRoom { get; } public IRoomManager RoomManager { get; } public OngoingOperationTracker OngoingOperationTracker { get; } public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; } @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay public OnlinePlayTestSceneDependencies() { - SelectedRoom = new Bindable(); + SelectedRoom = new Bindable(); RequestsHandler = new TestRoomRequestsHandler(); OngoingOperationTracker = new OngoingOperationTracker(); AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); From dc4581656602c5c1aca05f79902f584e412010a0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 20:41:14 +0900 Subject: [PATCH 67/73] Fix inspection --- .../OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs index 905a2bf31b..3186cf89a4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerSpectateButton.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match client.ToggleSpectate().ContinueWith(_ => endOperation()); - void endOperation() => clickOperation?.Dispose(); + void endOperation() => clickOperation.Dispose(); } [BackgroundDependencyLoader] From cc59434ea44d42726468f63668db0e4091e1116f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 20:52:34 +0900 Subject: [PATCH 68/73] Fix crash due to being too forgiving of nulls This one is super duper annoying to test, because we already have e.g. `TestScenePlaylistsScreen`. The only way to test it would be to use an `OsuGameTestScene`. Maybe this is okay? --- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 13ffb61d57..cc6a4e09e1 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -33,8 +33,8 @@ namespace osu.Game.Screens.OnlinePlay protected LoungeSubScreen Lounge { get; private set; } = null!; + private readonly ScreenStack screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }; private OnlinePlayScreenWaveContainer waves = null!; - private ScreenStack screenStack = null!; [Cached(Type = typeof(IRoomManager))] protected RoomManager RoomManager { get; private set; } @@ -65,7 +65,7 @@ namespace osu.Game.Screens.OnlinePlay RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }, + screenStack, new Header(ScreenTitle, screenStack), RoomManager, ongoingOperationTracker, From 209380cbac77939bc34e03459f376a5886f4fc30 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 21:03:12 +0900 Subject: [PATCH 69/73] Enable NRT in `TestMultiplayerRoomManager` --- .../Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 8d04c808fd..b998a638e5 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.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 osu.Game.Online.API.Requests.Responses; @@ -28,10 +26,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms; - public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) + public override void CreateRoom(Room room, Action? onSuccess = null, Action? onError = null) => base.CreateRoom(room, r => onSuccess?.Invoke(r), onError); - public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) + public override void JoinRoom(Room room, string? password = null, Action? onSuccess = null, Action? onError = null) => base.JoinRoom(room, password, r => onSuccess?.Invoke(r), onError); /// From 42f2ad2423df7b9cab73e387241279804db4710c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Nov 2024 13:06:01 +0100 Subject: [PATCH 70/73] Fix tests --- .../TestSceneBeatmapAttributeText.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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) { From 84ac3097c23dfad41d305cee13586fd09e4a72e1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Nov 2024 22:47:46 +0900 Subject: [PATCH 71/73] Populate parameter description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 0d9c210149..ffea3878fa 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -407,7 +407,7 @@ namespace osu.Game.Screens.OnlinePlay.Match /// /// Creates the gameplay screen to be entered. /// - /// + /// The playlist item about to be played. /// The screen to enter. protected abstract Screen CreateGameplayScreen(PlaylistItem selectedItem); From 5dffc322aff5485771de4c3f72cb1df7f8c522a8 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 21 Nov 2024 17:48:12 -0500 Subject: [PATCH 72/73] Recreate beatmap every test run in `ModTestScene` --- .../Mods/TestSceneCatchModNoScope.cs | 6 +- .../Mods/TestSceneCatchModRelax.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../Mods/TestSceneManiaModAutoplay.cs | 2 +- .../Mods/TestSceneManiaModDoubleTime.cs | 4 +- .../Mods/TestSceneManiaModFadeIn.cs | 2 +- .../Mods/TestSceneManiaModHidden.cs | 2 +- .../Mods/TestSceneManiaModPerfect.cs | 4 +- .../Mods/TestSceneManiaModSuddenDeath.cs | 4 +- .../Mods/TestSceneOsuModAlternate.cs | 8 +-- .../Mods/TestSceneOsuModAutoplay.cs | 4 +- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 2 +- .../Mods/TestSceneOsuModFlashlight.cs | 6 +- .../Mods/TestSceneOsuModHidden.cs | 8 +-- .../Mods/TestSceneOsuModMirror.cs | 2 +- .../Mods/TestSceneOsuModNoScope.cs | 6 +- .../Mods/TestSceneOsuModPerfect.cs | 2 +- .../Mods/TestSceneOsuModRandom.cs | 4 +- .../Mods/TestSceneOsuModSingleTap.cs | 8 +-- .../Mods/TestSceneOsuModSpunOut.cs | 2 +- .../Mods/TestSceneOsuModStrictTracking.cs | 2 +- .../Mods/TestSceneOsuModSuddenDeath.cs | 4 +- .../TestSceneMissHitWindowJudgements.cs | 14 ++--- .../Mods/TestSceneTaikoModHidden.cs | 60 ++++++++++--------- .../Mods/TestSceneTaikoModRelax.cs | 37 ++++++------ .../Mods/TestSceneTaikoModSingleTap.cs | 10 ++-- .../Mods/TestSceneModAccuracyChallenge.cs | 4 +- .../Tests/Visual/ModFailConditionTestScene.cs | 2 +- osu.Game/Tests/Visual/ModTestScene.cs | 4 +- 29 files changed, 110 insertions(+), 107 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs index c8f7da1aae..d185293093 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index a161615579..6114c9ce6f 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods Mod = new CatchModRelax(), Autoplay = false, PassCondition = passCondition, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index 825e8c697c..1f32a4531b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests { CreateModTest(new ModTestData { - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs index f653f209c1..4871f5b585 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Autoplay = true, - Beatmap = new ManiaBeatmap(new StageDefinition(1)) + Beatmap = () => new ManiaBeatmap(new StageDefinition(1)) { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs index 975e43ec08..a492c53456 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods && Precision.AlmostEquals(Player.ScoreProcessor.Accuracy.Value, 0.9836, 0.01) && Player.ScoreProcessor.TotalScore.Value == 946_049, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, Difficulty = { OverallDifficulty = 10 }, @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods && Player.ScoreProcessor.Accuracy.Value == 1 && Player.ScoreProcessor.TotalScore.Value == (long)(1_000_000 * doubleTime.ScoreMultiplier), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, Difficulty = { OverallDifficulty = 10 }, diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs index 9620897983..c425bb05c7 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Mod = new ManiaModHidden(), - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(), Breaks = { new BreakPeriod(2000, 28000) } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs index ae23c4573c..98cceefaf5 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Mod = new ManiaModHidden(), - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(), Breaks = { new BreakPeriod(2000, 28000) } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index 51730e2b43..7e2a0a4dd7 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs index 619816a815..c220578833 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index 7375617aa8..2192184eef 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 4, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index ace7f23989..5baafb4f79 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods CreateModTest(new ModTestData { Autoplay = true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Autoplay = true, Mod = mod, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 472c341bdd..0a97933df6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestNoAdjustment() => CreateModTest(new ModTestData { Mod = new OsuModDifficultyAdjust(), - Beatmap = new Beatmap + Beatmap = () => new Beatmap { BeatmapInfo = new BeatmapInfo { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs index 075fdd88ca..e413606a3d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime < 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && !sliderDimmedBeforeStartTime; }, - Beatmap = new OsuBeatmap + Beatmap = () => new OsuBeatmap { HitObjects = new List { @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && sliderDimmed; }, - Beatmap = new OsuBeatmap + Beatmap = () => new OsuBeatmap { HitObjects = new List { @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && sliderDimmed; }, - Beatmap = new OsuBeatmap + Beatmap = () => new OsuBeatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 58bdd805c1..fec4a748d3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, Autoplay = true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs index 0b3496ba68..c8e7061416 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestCorrectReflections([Values] OsuModMirror.MirrorType type, [Values] bool withStrictTracking) => CreateModTest(new ModTestData { Autoplay = true, - Beatmap = new OsuBeatmap + Beatmap = () => new OsuBeatmap { HitObjects = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs index d3996ebc3b..d7922f8ba7 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index b01bbbfca1..a9a94cfd72 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs index 060a845137..81d753fd2d 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs @@ -55,10 +55,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods PassCondition = () => true }); - private OsuBeatmap jumpBeatmap => + private OsuBeatmap jumpBeatmap() => createHitCircleBeatmap(new[] { 100, 200, 300, 400 }, 8, 300, 2 * 300); - private OsuBeatmap streamBeatmap => + private OsuBeatmap streamBeatmap() => createHitCircleBeatmap(new[] { 10, 20, 30, 40, 50, 60, 70, 80 }, 16, 150, 4 * 150); private OsuBeatmap createHitCircleBeatmap(IEnumerable spacings, int objectsPerSpacing, int interval, int beatLength) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs index bd2b205ac8..3d553fa051 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs index de3ea5f148..ced69d59de 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }); } - private Beatmap singleSpinnerBeatmap => new Beatmap + private Beatmap singleSpinnerBeatmap() => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs index 726b415977..d295b3d891 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModStrictTracking(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs index ea048aaa6e..25edda34d4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs index c37660831b..5164d7d01b 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Tests { Autoplay = false, Mod = new TestAutoMod(), - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }, @@ -47,18 +47,16 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestMissViaNotHitting() { - var beatmap = new Beatmap - { - HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } - }; - var hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); + hitWindows.SetDifficulty(IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY); CreateModTest(new ModTestData { Autoplay = false, - Beatmap = beatmap, + Beatmap = () => new Beatmap + { + HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } + }, PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit }); } diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs index 6e6be26e43..cf199f062a 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs @@ -31,40 +31,42 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { const double hit_time = 1; - var beatmap = new Beatmap - { - HitObjects = new List - { - new Hit - { - Type = HitType.Rim, - StartTime = hit_time, - }, - new Hit - { - Type = HitType.Centre, - StartTime = hit_time * 2, - }, - }, - BeatmapInfo = - { - Difficulty = new BeatmapDifficulty - { - SliderTickRate = 4, - OverallDifficulty = 0, - }, - Ruleset = new TaikoRuleset().RulesetInfo - }, - }; - - beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f }); - CreateModTest(new ModTestData { Mod = new TaikoModHidden(), Autoplay = true, PassCondition = checkAllMaxResultJudgements(2), - Beatmap = beatmap, + Beatmap = () => + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Hit + { + Type = HitType.Rim, + StartTime = hit_time, + }, + new Hit + { + Type = HitType.Centre, + StartTime = hit_time * 2, + }, + }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderTickRate = 4, + OverallDifficulty = 0, + }, + Ruleset = new TaikoRuleset().RulesetInfo + }, + }; + + beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f }); + return beatmap; + }, }); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs index caf8aa8e76..6894a19a1f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs @@ -15,7 +15,26 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods [Test] public void TestRelax() { - var beatmap = new TaikoBeatmap + var beatmapForReplay = createBeatmap(); + + foreach (var ho in beatmapForReplay.HitObjects) + ho.ApplyDefaults(beatmapForReplay.ControlPointInfo, beatmapForReplay.Difficulty); + + var replay = new TaikoAutoGenerator(beatmapForReplay).Generate(); + + foreach (var frame in replay.Frames.OfType().Where(r => r.Actions.Any())) + frame.Actions = [TaikoAction.LeftCentre]; + + CreateModTest(new ModTestData + { + Mod = new TaikoModRelax(), + Beatmap = createBeatmap, + ReplayFrames = replay.Frames, + Autoplay = false, + PassCondition = () => Player.ScoreProcessor.HasCompleted.Value && Player.ScoreProcessor.Accuracy.Value == 1, + }); + + TaikoBeatmap createBeatmap() => new TaikoBeatmap { HitObjects = { @@ -25,22 +44,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods new Swell { StartTime = 1250, Duration = 500 }, } }; - foreach (var ho in beatmap.HitObjects) - ho.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty); - - var replay = new TaikoAutoGenerator(beatmap).Generate(); - - foreach (var frame in replay.Frames.OfType().Where(r => r.Actions.Any())) - frame.Actions = [TaikoAction.LeftCentre]; - - CreateModTest(new ModTestData - { - Mod = new TaikoModRelax(), - Beatmap = beatmap, - ReplayFrames = replay.Frames, - Autoplay = false, - PassCondition = () => Player.ScoreProcessor.HasCompleted.Value && Player.ScoreProcessor.Accuracy.Value == 1, - }); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 3a11a91f82..840bf78ffe 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = new List { @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs index c5e56c6453..7011303ccd 100644 --- a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs +++ b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Mods MinimumAccuracy = { Value = 0.6 } }, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle { @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Mods AccuracyJudgeMode = { Value = ModAccuracyChallenge.AccuracyMode.Standard } }, Autoplay = false, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle { diff --git a/osu.Game/Tests/Visual/ModFailConditionTestScene.cs b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs index 8f0dff055d..364145160d 100644 --- a/osu.Game/Tests/Visual/ModFailConditionTestScene.cs +++ b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual protected void CreateHitObjectTest(HitObjectTestData testData, bool shouldMiss) => CreateModTest(new ModTestData { Mod = mod, - Beatmap = new Beatmap + Beatmap = () => new Beatmap { BeatmapInfo = { Ruleset = CreatePlayerRuleset().RulesetInfo }, HitObjects = { testData.HitObject } diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index c2ebcdefac..3265e5c257 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual base.TearDownSteps(); } - protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => CurrentTestData?.Beatmap ?? base.CreateBeatmap(ruleset); + protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => CurrentTestData?.Beatmap?.Invoke() ?? base.CreateBeatmap(ruleset); protected sealed override TestPlayer CreatePlayer(Ruleset ruleset) { @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual /// The beatmap for this test case. /// [CanBeNull] - public IBeatmap Beatmap; + public Func Beatmap; /// /// The conditions that cause this test case to pass. From 41addaae9a26575482f6682ce3b450d9420c1c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Nov 2024 13:11:32 +0900 Subject: [PATCH 73/73] Rename variable to imply it is now a function --- .../Mods/TestSceneCatchModNoScope.cs | 6 +++--- .../Mods/TestSceneCatchModRelax.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../Mods/TestSceneManiaModAutoplay.cs | 2 +- .../Mods/TestSceneManiaModDoubleTime.cs | 4 ++-- .../Mods/TestSceneManiaModFadeIn.cs | 2 +- .../Mods/TestSceneManiaModHidden.cs | 2 +- .../Mods/TestSceneManiaModPerfect.cs | 4 ++-- .../Mods/TestSceneManiaModSuddenDeath.cs | 4 ++-- .../Mods/TestSceneOsuModAlternate.cs | 8 ++++---- .../Mods/TestSceneOsuModAutoplay.cs | 4 ++-- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 2 +- .../Mods/TestSceneOsuModFlashlight.cs | 6 +++--- .../Mods/TestSceneOsuModHidden.cs | 8 ++++---- .../Mods/TestSceneOsuModMirror.cs | 2 +- .../Mods/TestSceneOsuModNoScope.cs | 6 +++--- .../Mods/TestSceneOsuModPerfect.cs | 2 +- .../Mods/TestSceneOsuModRandom.cs | 4 ++-- .../Mods/TestSceneOsuModSingleTap.cs | 8 ++++---- .../Mods/TestSceneOsuModSpunOut.cs | 6 +++--- .../Mods/TestSceneOsuModStrictTracking.cs | 2 +- .../Mods/TestSceneOsuModSuddenDeath.cs | 4 ++-- .../TestSceneMissHitWindowJudgements.cs | 4 ++-- .../Mods/TestSceneTaikoModHidden.cs | 2 +- .../Mods/TestSceneTaikoModRelax.cs | 2 +- .../Mods/TestSceneTaikoModSingleTap.cs | 10 +++++----- .../Visual/Mods/TestSceneModAccuracyChallenge.cs | 4 ++-- osu.Game/Tests/Visual/ModFailConditionTestScene.cs | 2 +- osu.Game/Tests/Visual/ModTestScene.cs | 9 +++++---- 29 files changed, 62 insertions(+), 61 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs index d185293093..828e47e187 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModNoScope.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index 6114c9ce6f..92f7960183 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods Mod = new CatchModRelax(), Autoplay = false, PassCondition = passCondition, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index 1f32a4531b..9365659f79 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests { CreateModTest(new ModTestData { - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs index 4871f5b585..d644ef359d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Autoplay = true, - Beatmap = () => new ManiaBeatmap(new StageDefinition(1)) + CreateBeatmap = () => new ManiaBeatmap(new StageDefinition(1)) { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs index a492c53456..50ec119e4d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods && Precision.AlmostEquals(Player.ScoreProcessor.Accuracy.Value, 0.9836, 0.01) && Player.ScoreProcessor.TotalScore.Value == 946_049, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, Difficulty = { OverallDifficulty = 10 }, @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods && Player.ScoreProcessor.Accuracy.Value == 1 && Player.ScoreProcessor.TotalScore.Value == (long)(1_000_000 * doubleTime.ScoreMultiplier), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }, Difficulty = { OverallDifficulty = 10 }, diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs index c425bb05c7..f403d67377 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModFadeIn.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Mod = new ManiaModHidden(), - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(), Breaks = { new BreakPeriod(2000, 28000) } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs index 98cceefaf5..e7e1cf3c7c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHidden.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods CreateModTest(new ModTestData { Mod = new ManiaModHidden(), - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(), Breaks = { new BreakPeriod(2000, 28000) } diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index 7e2a0a4dd7..b7a9b31dcc 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs index c220578833..15c49ab0a0 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = new ManiaModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs index 2192184eef..27ff26b438 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAlternate.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 4, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModAlternate(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index 5baafb4f79..8786b17b92 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods CreateModTest(new ModTestData { Autoplay = true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Autoplay = true, Mod = mod, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 0a97933df6..ca752fe918 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestNoAdjustment() => CreateModTest(new ModTestData { Mod = new OsuModDifficultyAdjust(), - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { BeatmapInfo = new BeatmapInfo { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs index e413606a3d..6bd3f25bdb 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime < 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && !sliderDimmedBeforeStartTime; }, - Beatmap = () => new OsuBeatmap + CreateBeatmap = () => new OsuBeatmap { HitObjects = new List { @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && sliderDimmed; }, - Beatmap = () => new OsuBeatmap + CreateBeatmap = () => new OsuBeatmap { HitObjects = new List { @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; return Player.GameplayState.HasPassed && sliderDimmed; }, - Beatmap = () => new OsuBeatmap + CreateBeatmap = () => new OsuBeatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index fec4a748d3..c513f98f21 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new TestOsuModHidden(), Autoplay = true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, Autoplay = true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs index c8e7061416..076cb9ae15 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMirror.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestCorrectReflections([Values] OsuModMirror.MirrorType type, [Values] bool withStrictTracking) => CreateModTest(new ModTestData { Autoplay = true, - Beatmap = () => new OsuBeatmap + CreateBeatmap = () => new OsuBeatmap { HitObjects = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs index d7922f8ba7..b9559aeba3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }, Autoplay = true, PassCondition = () => true, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index a9a94cfd72..8498e53bf0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModPerfect(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs index 81d753fd2d..3456fcbe84 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRandom.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { AngleSharpness = { Value = angleSharpness } }, - Beatmap = jumpBeatmap, + CreateBeatmap = jumpBeatmap, Autoplay = true, PassCondition = () => true }); @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { AngleSharpness = { Value = angleSharpness } }, - Beatmap = streamBeatmap, + CreateBeatmap = streamBeatmap, Autoplay = true, PassCondition = () => true }); diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs index 3d553fa051..b0be70e85e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSingleTap(), PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs index ced69d59de..3706b9ac07 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModSpunOut(), Autoplay = false, - Beatmap = singleSpinnerBeatmap, + CreateBeatmap = singleSpinnerBeatmap, PassCondition = () => { // Bind to the first spinner's results for further tracking. @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mods = mods, Autoplay = false, - Beatmap = singleSpinnerBeatmap, + CreateBeatmap = singleSpinnerBeatmap, PassCondition = () => { var counter = Player.ChildrenOfType().SingleOrDefault(); @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModSpunOut(), Autoplay = false, - Beatmap = singleSpinnerBeatmap, + CreateBeatmap = singleSpinnerBeatmap, PassCondition = () => { // Bind to the first spinner's results for further tracking. diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs index d295b3d891..66a60e3542 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { Mod = new OsuModStrictTracking(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs index 25edda34d4..688cf70f71 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSuddenDeath(), PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs index 5164d7d01b..7a89140fc4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneMissHitWindowJudgements.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Tests { Autoplay = false, Mod = new TestAutoMod(), - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }, @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Tests CreateModTest(new ModTestData { Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }, diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs index cf199f062a..e6d5c51902 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods Mod = new TaikoModHidden(), Autoplay = true, PassCondition = checkAllMaxResultJudgements(2), - Beatmap = () => + CreateBeatmap = () => { var beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs index 6894a19a1f..fffe42f1f8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModRelax.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods CreateModTest(new ModTestData { Mod = new TaikoModRelax(), - Beatmap = createBeatmap, + CreateBeatmap = createBeatmap, ReplayFrames = replay.Frames, Autoplay = false, PassCondition = () => Player.ScoreProcessor.HasCompleted.Value && Player.ScoreProcessor.Accuracy.Value == 1, diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs index 840bf78ffe..b12ac10d2d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModSingleTap.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = new List { @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods { Mod = new TaikoModSingleTap(), Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { Breaks = { diff --git a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs index 7011303ccd..e1c15863ad 100644 --- a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs +++ b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Mods MinimumAccuracy = { Value = 0.6 } }, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle { @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Mods AccuracyJudgeMode = { Value = ModAccuracyChallenge.AccuracyMode.Standard } }, Autoplay = false, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle { diff --git a/osu.Game/Tests/Visual/ModFailConditionTestScene.cs b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs index 364145160d..72bdd54c51 100644 --- a/osu.Game/Tests/Visual/ModFailConditionTestScene.cs +++ b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual protected void CreateHitObjectTest(HitObjectTestData testData, bool shouldMiss) => CreateModTest(new ModTestData { Mod = mod, - Beatmap = () => new Beatmap + CreateBeatmap = () => new Beatmap { BeatmapInfo = { Ruleset = CreatePlayerRuleset().RulesetInfo }, HitObjects = { testData.HitObject } diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index 3265e5c257..eb61518d35 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual base.TearDownSteps(); } - protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => CurrentTestData?.Beatmap?.Invoke() ?? base.CreateBeatmap(ruleset); + protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => CurrentTestData?.CreateBeatmap?.Invoke() ?? base.CreateBeatmap(ruleset); protected sealed override TestPlayer CreatePlayer(Ruleset ruleset) { @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual protected class ModTestData { /// - /// Whether to use a replay to simulate an auto-play. True by default. + /// Whether to use a replay to simulate an autoplay. True by default. /// public bool Autoplay = true; @@ -104,10 +104,11 @@ namespace osu.Game.Tests.Visual public List ReplayFrames; /// - /// The beatmap for this test case. + /// A function which should create a new instance of a beatmap containing relevant + /// content to the test. /// [CanBeNull] - public Func Beatmap; + public Func CreateBeatmap; /// /// The conditions that cause this test case to pass.