From 3b600f0a7b2dda8fa17ba85ee1563526bad44ece Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 10:45:19 +0300 Subject: [PATCH 001/198] Target net5.0 instead of netcoreapp3 --- global.json | 2 +- osu.Desktop/osu.Desktop.csproj | 2 +- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/global.json b/global.json index 10b61047ac..2cb4c02970 100644 --- a/global.json +++ b/global.json @@ -2,7 +2,7 @@ "sdk": { "allowPrerelease": false, "rollForward": "minor", - "version": "3.1.100" + "version": "5.0.100" }, "msbuild-sdks": { "Microsoft.Build.Traversal": "2.2.3" diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 62e8f7c518..7f5154f456 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 WinExe true A free-to-win rhythm game. Rhythm is just a *click* away! diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index ff26f4afaa..7805bfcefc 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 Exe false diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index dfe3bf8af4..a51b9830be 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -9,7 +9,7 @@ WinExe - netcoreapp3.1 + net5.0 diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 892f27d27f..d314671bce 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -9,7 +9,7 @@ WinExe - netcoreapp3.1 + net5.0 diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 3639c3616f..b0799bd3f5 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -9,7 +9,7 @@ WinExe - netcoreapp3.1 + net5.0 diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index b59f3a4344..d3dbba4bfc 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -9,7 +9,7 @@ WinExe - netcoreapp3.1 + net5.0 diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index c692bcd5e4..34de54411b 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -10,7 +10,7 @@ WinExe - netcoreapp3.1 + net5.0 diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 5d55196dcf..d820794980 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -11,7 +11,7 @@ WinExe - netcoreapp3.1 + net5.0 From ca0e1c8cee1ffd03b2bb5fe773741012b90bb654 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 10:47:57 +0300 Subject: [PATCH 002/198] Update NuGet packages --- osu.Desktop/osu.Desktop.csproj | 4 ++-- osu.Game.Tournament/osu.Game.Tournament.csproj | 2 +- osu.Game/osu.Game.csproj | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 7f5154f456..2052c4bc25 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,11 +24,11 @@ - + - + diff --git a/osu.Game.Tournament/osu.Game.Tournament.csproj b/osu.Game.Tournament/osu.Game.Tournament.csproj index 9cce40c9d3..b049542bb0 100644 --- a/osu.Game.Tournament/osu.Game.Tournament.csproj +++ b/osu.Game.Tournament/osu.Game.Tournament.csproj @@ -9,6 +9,6 @@ - + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 54f3fcede6..1670bf5b11 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,8 +21,8 @@ - - + + @@ -31,6 +31,6 @@ - + From f562a7ea0df68baee20551108bb4bd9fd13d5747 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 11:52:17 +0300 Subject: [PATCH 003/198] Fix FileNotFoundException on startup --- osu.Desktop/osu.Desktop.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 2052c4bc25..53b9cdcf92 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,6 +24,8 @@ + + From 044622036cbc94adae124f68ec5fbff18befb2b4 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 11:53:17 +0300 Subject: [PATCH 004/198] Disable CA1416 --- osu.Desktop/OsuGameDesktop.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 0feab9a717..e2a06d7877 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -58,8 +58,10 @@ namespace osu.Desktop try { +#pragma warning disable CA1416 // Validate platform compatibility using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); +#pragma warning restore CA1416 // Validate platform compatibility if (checkExists(stableInstallPath)) return stableInstallPath; From 1a676ef0d82c4d4cfd0d7fbac2338da3a763272f Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 12:06:08 +0300 Subject: [PATCH 005/198] Resolve CA1416 properly using new API --- osu.Desktop/OsuGameDesktop.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e2a06d7877..f9c932b260 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Versioning; using System.Threading.Tasks; using Microsoft.Win32; using osu.Desktop.Overlays; @@ -56,19 +57,12 @@ namespace osu.Desktop string stableInstallPath; - try + if (OperatingSystem.IsWindows()) { -#pragma warning disable CA1416 // Validate platform compatibility - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); -#pragma warning restore CA1416 // Validate platform compatibility - + stableInstallPath = getStableInstallPathFromRegistry(); if (checkExists(stableInstallPath)) return stableInstallPath; } - catch - { - } stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) @@ -81,6 +75,13 @@ namespace osu.Desktop return null; } + [SupportedOSPlatform("windows")] + private string getStableInstallPathFromRegistry() + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); + } + protected override UpdateManager CreateUpdateManager() { switch (RuntimeInfo.OS) From 743541649706d9b9165197421860dfd0b3cf264d Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 14:13:16 +0300 Subject: [PATCH 006/198] Workaround FileNotFoundException in a test projects --- osu.Desktop/osu.Desktop.csproj | 2 -- osu.Game/osu.Game.csproj | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 53b9cdcf92..2052c4bc25 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,8 +24,6 @@ - - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1670bf5b11..1c6139b519 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,6 +18,8 @@ + + From 1feda1152da0b15835af3ce83bed31faf5fbed65 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Sat, 21 Nov 2020 02:06:20 +0300 Subject: [PATCH 007/198] Fix InspectCode warnings --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 5 +++++ osu.Game.Tournament/IPC/FileBasedIPC.cs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f9c932b260..dbbf6d048b 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -79,7 +79,7 @@ namespace osu.Desktop private string getStableInstallPathFromRegistry() { using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); + return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", ""); } protected override UpdateManager CreateUpdateManager() diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 35473ee76c..58992366ff 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -73,6 +74,8 @@ namespace osu.Game.Tests.Visual.Gameplay switch (args.Action) { case NotifyCollectionChangedAction.Add: + Debug.Assert(args.NewItems != null, "args.NewItems != null"); + foreach (int user in args.NewItems) { if (user == api.LocalUser.Value.Id) @@ -82,6 +85,8 @@ namespace osu.Game.Tests.Visual.Gameplay break; case NotifyCollectionChangedAction.Remove: + Debug.Assert(args.OldItems != null, "args.OldItems != null"); + foreach (int user in args.OldItems) { if (user == api.LocalUser.Value.Id) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 999ce61ac8..99147951b2 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -243,7 +243,7 @@ namespace osu.Game.Tournament.IPC string stableInstallPath; using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString().Split('"')[1].Replace("osu!.exe", ""); if (ipcFileExistsInDirectory(stableInstallPath)) return stableInstallPath; From eda6e1fbddb46b9eec3a33a1417581154645ff8a Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 13:11:13 +0100 Subject: [PATCH 008/198] Add tournament switching in the UI --- osu.Game.Tournament/IO/TournamentStorage.cs | 18 ++++++++++++++++++ osu.Game.Tournament/Screens/SetupScreen.cs | 13 +++++++++++++ 2 files changed, 31 insertions(+) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 2e8a6ce667..66e27ddbb5 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.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 osu.Framework.Bindables; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.IO; using System.IO; +using System.Collections.Generic; using osu.Game.Tournament.Configuration; namespace osu.Game.Tournament.IO @@ -13,12 +15,15 @@ namespace osu.Game.Tournament.IO { private const string default_tournament = "default"; private readonly Storage storage; + private readonly Storage allTournaments; private readonly TournamentStorageManager storageConfig; + public readonly Bindable CurrentTournament; public TournamentStorage(Storage storage) : base(storage.GetStorageForDirectory("tournaments"), string.Empty) { this.storage = storage; + allTournaments = UnderlyingStorage; storageConfig = new TournamentStorageManager(storage); @@ -29,9 +34,22 @@ namespace osu.Game.Tournament.IO else Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); + CurrentTournament = new Bindable(storageConfig.Get(StorageConfig.CurrentTournament)); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); + + CurrentTournament.BindValueChanged(updateTournament, false); } + private void updateTournament(ValueChangedEvent newTournament) + { + ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue)); + Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); + storageConfig.Set(StorageConfig.CurrentTournament, newTournament.NewValue); + storageConfig.Save(); + } + + public IEnumerable ListTournaments() => allTournaments.GetDirectories(string.Empty); + public override void Migrate(Storage newStorage) { // this migration only happens once on moving to the per-tournament storage system. diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index e78d3a9e83..9f34c36e64 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -9,8 +9,10 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Tournament.IO; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets; @@ -40,6 +42,9 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private Storage storage { get; set; } + [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -70,6 +75,7 @@ namespace osu.Game.Tournament.Screens private void reload() { var fileBasedIpc = ipc as FileBasedIPC; + var tourneyStorage = storage as TournamentStorage; fillFlow.Children = new Drawable[] { new ActionableInfo @@ -111,6 +117,13 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, + new LabelledDropdown + { + Label = "Current tournament", + Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart after selecting to apply changes.", + Items = tourneyStorage?.ListTournaments(), + Current = tourneyStorage?.CurrentTournament, + }, resolution = new ResolutionSelector { Label = "Stream area resolution", From 191f863a494dcbdab5aabb92c0686ddded29fabf Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 13:14:59 +0100 Subject: [PATCH 009/198] Remove unncessary words from the description --- osu.Game.Tournament/Screens/SetupScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 9f34c36e64..f22cd13cea 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tournament.Screens new LabelledDropdown { Label = "Current tournament", - Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart after selecting to apply changes.", + Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", Items = tourneyStorage?.ListTournaments(), Current = tourneyStorage?.CurrentTournament, }, From f980f413244c3252890868df1f32c2f1c88d3f81 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 21:38:15 +0100 Subject: [PATCH 010/198] Address review --- osu.Game.Tournament/IO/TournamentStorage.cs | 6 ++---- osu.Game.Tournament/Screens/SetupScreen.cs | 12 +++++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index 66e27ddbb5..cf54c1f5ef 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -34,18 +34,16 @@ namespace osu.Game.Tournament.IO else Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); - CurrentTournament = new Bindable(storageConfig.Get(StorageConfig.CurrentTournament)); + CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); - CurrentTournament.BindValueChanged(updateTournament, false); + CurrentTournament.BindValueChanged(updateTournament); } private void updateTournament(ValueChangedEvent newTournament) { ChangeTargetStorage(allTournaments.GetStorageForDirectory(newTournament.NewValue)); Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty)); - storageConfig.Set(StorageConfig.CurrentTournament, newTournament.NewValue); - storageConfig.Save(); } public IEnumerable ListTournaments() => allTournaments.GetDirectories(string.Empty); diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index f22cd13cea..3c9d3c949b 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,16 +42,14 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } - [Resolved] - private Storage storage { get; set; } - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } private Bindable windowSize; + private TournamentStorage storage; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig) + private void load(FrameworkConfigManager frameworkConfig, Storage storage) { windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); @@ -66,6 +64,7 @@ namespace osu.Game.Tournament.Screens api.LocalUser.BindValueChanged(_ => Schedule(reload)); stableInfo.OnStableInfoSaved += () => Schedule(reload); + this.storage = (TournamentStorage)storage; reload(); } @@ -75,7 +74,6 @@ namespace osu.Game.Tournament.Screens private void reload() { var fileBasedIpc = ipc as FileBasedIPC; - var tourneyStorage = storage as TournamentStorage; fillFlow.Children = new Drawable[] { new ActionableInfo @@ -121,8 +119,8 @@ namespace osu.Game.Tournament.Screens { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", - Items = tourneyStorage?.ListTournaments(), - Current = tourneyStorage?.CurrentTournament, + Items = storage.ListTournaments(), + Current = storage.CurrentTournament, }, resolution = new ResolutionSelector { From 6002014f952ee71992f54c267ed4c9967e5d5639 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 7 Dec 2020 22:07:54 +0100 Subject: [PATCH 011/198] Change underlyingstorage to alltournaments for clarity --- osu.Game.Tournament/IO/TournamentStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/IO/TournamentStorage.cs b/osu.Game.Tournament/IO/TournamentStorage.cs index cf54c1f5ef..2ba1b6be8f 100644 --- a/osu.Game.Tournament/IO/TournamentStorage.cs +++ b/osu.Game.Tournament/IO/TournamentStorage.cs @@ -29,10 +29,10 @@ namespace osu.Game.Tournament.IO if (storage.Exists("tournament.ini")) { - ChangeTargetStorage(UnderlyingStorage.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); + ChangeTargetStorage(allTournaments.GetStorageForDirectory(storageConfig.Get(StorageConfig.CurrentTournament))); } else - Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament)); + Migrate(allTournaments.GetStorageForDirectory(default_tournament)); CurrentTournament = storageConfig.GetBindable(StorageConfig.CurrentTournament); Logger.Log("Using tournament storage: " + GetFullPath(string.Empty)); From 9e15dccc56bfcdde0f36a6c15db413faff07b06b Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 26 Dec 2020 15:36:21 +0100 Subject: [PATCH 012/198] Move graceful exit to OsuGameBase --- osu.Game/OsuGame.cs | 12 ------------ osu.Game/OsuGameBase.cs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bb638bcf3a..710dfd7031 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -918,18 +918,6 @@ namespace osu.Game return base.OnExiting(); } - /// - /// Use to programatically exit the game as if the user was triggering via alt-f4. - /// Will keep persisting until an exit occurs (exit may be blocked multiple times). - /// - public void GracefullyExit() - { - if (!OnExiting()) - Exit(); - else - Scheduler.AddDelayed(GracefullyExit, 2000); - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index e7b5d3304d..91ab2bdc1e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -368,6 +368,18 @@ namespace osu.Game LocalConfig ??= new OsuConfigManager(Storage); } + /// + /// Use to programatically exit the game as if the user was triggering via alt-f4. + /// Will keep persisting until an exit occurs (exit may be blocked multiple times). + /// + public void GracefullyExit() + { + if (!OnExiting()) + Exit(); + else + Scheduler.AddDelayed(GracefullyExit, 2000); + } + protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage); private readonly List fileImporters = new List(); From 8e428353ee0ae94e7efd7995ddbd01317842b7f7 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sat, 26 Dec 2020 15:44:59 +0100 Subject: [PATCH 013/198] Revise TournamentSwitcher to include a close button --- osu.Game.Tournament/Screens/SetupScreen.cs | 44 ++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 3c9d3c949b..de3321397e 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,6 +42,9 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } + [Resolved] + private TournamentGameBase game { get; set; } + [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } @@ -68,9 +71,6 @@ namespace osu.Game.Tournament.Screens reload(); } - [Resolved] - private Framework.Game game { get; set; } - private void reload() { var fileBasedIpc = ipc as FileBasedIPC; @@ -115,12 +115,14 @@ namespace osu.Game.Tournament.Screens Items = rulesets.AvailableRulesets, Current = LadderInfo.Ruleset, }, - new LabelledDropdown + new TournamentSwitcher { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", Items = storage.ListTournaments(), Current = storage.CurrentTournament, + ButtonText = "Close osu!", + Action = () => game.GracefullyExit() }, resolution = new ResolutionSelector { @@ -165,7 +167,7 @@ namespace osu.Game.Tournament.Screens private class ActionableInfo : LabelledDrawable { - private OsuButton button; + protected OsuButton Button; public ActionableInfo() : base(true) @@ -174,7 +176,7 @@ namespace osu.Game.Tournament.Screens public string ButtonText { - set => button.Text = value; + set => Button.Text = value; } public string Value @@ -211,7 +213,7 @@ namespace osu.Game.Tournament.Screens Spacing = new Vector2(10, 0), Children = new Drawable[] { - button = new TriangleButton + Button = new TriangleButton { Size = new Vector2(100, 40), Action = () => Action?.Invoke() @@ -222,6 +224,34 @@ namespace osu.Game.Tournament.Screens }; } + private class TournamentSwitcher : ActionableInfo + { + private OsuDropdown dropdown; + + public IEnumerable Items + { + get => dropdown.Items; + set => dropdown.Items = value; + } + + public Bindable Current + { + get => dropdown.Current; + set => dropdown.Current = value; + } + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, dropdown = new OsuDropdown + { + Width = 510 + }); + + return drawable; + } + } + private class ResolutionSelector : ActionableInfo { private const int minimum_window_height = 480; From 9e6994166c0eef43cb63b6d1d34e70e0cb324634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 20:59:12 +0100 Subject: [PATCH 014/198] Add helper to track ongoing operations in UI --- .../OnlinePlay/OngoingOperationTracker.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs new file mode 100644 index 0000000000..f2d943e14f --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -0,0 +1,47 @@ +// 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.Bindables; + +namespace osu.Game.Screens.OnlinePlay +{ + /// + /// Utility class to track ongoing online operations' progress. + /// Can be used to disable interactivity while waiting for a response from online sources. + /// + public class OngoingOperationTracker + { + /// + /// Whether there is an online operation in progress. + /// + public IBindable InProgress => inProgress; + + private readonly Bindable inProgress = new BindableBool(); + + private LeasedBindable leasedInProgress; + + /// + /// Begins tracking a new online operation. + /// + /// An operation has already been started. + public void BeginOperation() + { + if (leasedInProgress != null) + throw new InvalidOperationException("Cannot begin operation while another is in progress."); + + leasedInProgress = inProgress.BeginLease(true); + leasedInProgress.Value = true; + } + + /// + /// Ends tracking an online operation. + /// Does nothing if an operation has not been begun yet. + /// + public void EndOperation() + { + leasedInProgress?.Return(); + leasedInProgress = null; + } + } +} From 47ab7c9fd67270079d39e8ad9f45fb3e986c76df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 20:59:38 +0100 Subject: [PATCH 015/198] Disable ready button after host click --- .../TestSceneMultiplayerReadyButton.cs | 22 +++++++++++++++---- .../Match/MultiplayerReadyButton.cs | 13 ++++++++++- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 6b11613f1c..958c6d218b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -7,11 +7,14 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; @@ -27,6 +30,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; + [Cached] + private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -89,8 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer addClickButtonStep(); AddAssert("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); - addClickButtonStep(); - AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + verifyGameplayStartFlow(); } [Test] @@ -105,8 +110,7 @@ namespace osu.Game.Tests.Visual.Multiplayer addClickButtonStep(); AddStep("make user host", () => Client.TransferHost(Client.Room?.Users[0].UserID ?? 0)); - addClickButtonStep(); - AddAssert("match started", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + verifyGameplayStartFlow(); } [Test] @@ -160,5 +164,15 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.MoveMouseTo(button); InputManager.Click(MouseButton.Left); }); + + private void verifyGameplayStartFlow() + { + addClickButtonStep(); + AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); + AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); + + AddStep("transitioned to gameplay", () => gameplayStartTracker.EndOperation()); + AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 281e92404c..5009b435f4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -33,11 +33,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private OsuColour colours { get; set; } + [Resolved] + private OngoingOperationTracker gameplayStartTracker { get; set; } + private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; + private IBindable gameplayStartInProgress; public MultiplayerReadyButton() { @@ -54,6 +58,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void load(AudioManager audio) { sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); + + gameplayStartInProgress = gameplayStartTracker.InProgress.GetBoundCopy(); + gameplayStartInProgress.BindValueChanged(_ => updateState()); } protected override void OnRoomUpdated() @@ -63,7 +70,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // this method is called on leaving the room, so the local user may not exist in the room any more. localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); - button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open; updateState(); } @@ -100,6 +106,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !gameplayStartInProgress.Value; + if (newCountReady != countReady) { countReady = newCountReady; @@ -142,7 +150,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match else { if (Room?.Host?.Equals(localUser) == true) + { + gameplayStartTracker.BeginOperation(); Client.StartMatch().CatchUnobservedExceptions(true); + } else Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 58314c3774..93db913ce0 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -31,6 +31,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + [Cached] + private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + private MultiplayerMatchSettingsOverlay settingsOverlay; private IBindable isConnected; @@ -203,6 +206,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); + gameplayStartTracker.EndOperation(); } protected override void Dispose(bool isDisposing) From af66e4531196853215e8625cf858d4a0d221aaec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 21:39:11 +0100 Subject: [PATCH 016/198] Disable create room button after triggering join --- .../TestSceneCreateMultiplayerMatchButton.cs | 52 +++++++++++++++++++ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 22 ++++---- .../CreateMultiplayerMatchButton.cs | 19 ++++++- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 3 ++ 4 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs new file mode 100644 index 0000000000..2a549e5262 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Screens.OnlinePlay; +using osu.Game.Screens.OnlinePlay.Multiplayer; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene + { + [Cached] + private OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + + private CreateMultiplayerMatchButton button; + + public override void SetUpSteps() + { + base.SetUpSteps(); + AddStep("create button", () => Child = button = new CreateMultiplayerMatchButton + { + Width = 200, + Height = 100, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + } + + [Test] + public void TestButtonEnableStateChanges() + { + assertButtonEnableState(true); + + AddStep("begin joining room", () => joiningRoomTracker.BeginOperation()); + assertButtonEnableState(false); + + AddStep("end joining room", () => joiningRoomTracker.EndOperation()); + assertButtonEnableState(true); + + AddStep("disconnect client", () => Client.Disconnect()); + assertButtonEnableState(false); + + AddStep("re-connect client", () => Client.Connect()); + assertButtonEnableState(true); + } + + private void assertButtonEnableState(bool enabled) + => AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => button.Enabled.Value == enabled); + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 79f5dfdee1..2730693f73 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -26,6 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); private readonly IBindable initialRoomsReceived = new Bindable(); + private readonly IBindable joiningRoom = new Bindable(); private FilterControl filter; private Container content; @@ -37,7 +38,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private MusicController music { get; set; } - private bool joiningRoom; + [Resolved] + private OngoingOperationTracker joiningRoomTracker { get; set; } [BackgroundDependencyLoader] private void load() @@ -98,7 +100,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); - initialRoomsReceived.BindValueChanged(onInitialRoomsReceivedChanged, true); + initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); + + joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); } protected override void UpdateAfterChildren() @@ -156,26 +161,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoom = true; - updateLoadingLayer(); + joiningRoomTracker.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoom = false; - updateLoadingLayer(); + joiningRoomTracker.EndOperation(); }, _ => { - joiningRoom = false; - updateLoadingLayer(); + joiningRoomTracker.EndOperation(); }); } - private void onInitialRoomsReceivedChanged(ValueChangedEvent received) => updateLoadingLayer(); - private void updateLoadingLayer() { - if (joiningRoom || !initialRoomsReceived.Value) + if (joiningRoom.Value || !initialRoomsReceived.Value) loadingLayer.Show(); else loadingLayer.Hide(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 163efd9c20..7518a4e71b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -10,14 +10,29 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class CreateMultiplayerMatchButton : PurpleTriangleButton { + private IBindable isConnected; + private IBindable joiningRoom; + + [Resolved] + private StatefulMultiplayerClient multiplayerClient { get; set; } + + [Resolved] + private OngoingOperationTracker joiningRoomTracker { get; set; } + [BackgroundDependencyLoader] - private void load(StatefulMultiplayerClient multiplayerClient) + private void load() { Triangles.TriangleScale = 1.5f; Text = "Create room"; - ((IBindable)Enabled).BindTo(multiplayerClient.IsConnected); + isConnected = multiplayerClient.IsConnected.GetBoundCopy(); + isConnected.BindValueChanged(_ => updateState()); + + joiningRoom = joiningRoomTracker.InProgress.GetBoundCopy(); + joiningRoom.BindValueChanged(_ => updateState(), true); } + + private void updateState() => Enabled.Value = isConnected.Value && !joiningRoom.Value; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 4074dd1573..92665c498b 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -52,6 +52,9 @@ namespace osu.Game.Screens.OnlinePlay [Cached] private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); + [Cached] + private readonly OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + [Resolved(CanBeNull = true)] private MusicController music { get; set; } From 6dc0f6af50a3befd707a7f9d4a5fe10cd0862413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 21:44:58 +0100 Subject: [PATCH 017/198] Disable setting apply button for duration of operation --- .../Match/MultiplayerMatchSettingsOverlay.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index ae03d384f6..de25cff546 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -68,6 +68,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private Bindable ruleset { get; set; } + private readonly OngoingOperationTracker applyingSettingsTracker = new OngoingOperationTracker(); + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -274,13 +276,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true); MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); + + applyingSettingsTracker.InProgress.BindValueChanged(v => + { + if (v.NewValue) + loadingLayer.Show(); + else + loadingLayer.Hide(); + }); } protected override void Update() { base.Update(); - ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0; + ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !applyingSettingsTracker.InProgress.Value; } private void apply() @@ -289,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match return; hideError(); - loadingLayer.Show(); + applyingSettingsTracker.BeginOperation(); // 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. @@ -322,7 +332,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onSuccess(Room room) { - loadingLayer.Hide(); + applyingSettingsTracker.EndOperation(); SettingsApplied?.Invoke(); } @@ -330,8 +340,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { ErrorText.Text = text; ErrorText.FadeIn(50); - - loadingLayer.Hide(); + applyingSettingsTracker.EndOperation(); } } From 540dec2e7ccbb96342683545d744ffa74b4011d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 28 Dec 2020 22:54:52 +0100 Subject: [PATCH 018/198] Allow null tracker in lounge screen for tests --- .../Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 2730693f73..f25835f56a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved] private MusicController music { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private OngoingOperationTracker joiningRoomTracker { get; set; } [BackgroundDependencyLoader] @@ -102,8 +102,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); - joiningRoom.BindTo(joiningRoomTracker.InProgress); - joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + if (joiningRoomTracker != null) + { + joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + } } protected override void UpdateAfterChildren() @@ -161,15 +164,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoomTracker.BeginOperation(); + joiningRoomTracker?.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoomTracker.EndOperation(); + joiningRoomTracker?.EndOperation(); }, _ => { - joiningRoomTracker.EndOperation(); + joiningRoomTracker?.EndOperation(); }); } From 903dca875e7aa364277dc336e52c9c350a6f363a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:46:22 +0100 Subject: [PATCH 019/198] Make localUser a client property --- .../Online/Multiplayer/StatefulMultiplayerClient.cs | 5 +++++ .../Multiplayer/Match/MultiplayerReadyButton.cs | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 39d119b2a4..0f44085134 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -65,6 +65,11 @@ namespace osu.Game.Online.Multiplayer /// public readonly BindableList CurrentMatchPlayingUserIds = new BindableList(); + /// + /// The corresponding to the local player, if available. + /// + public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); + [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 5009b435f4..9b406ff52c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -27,9 +27,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private IAPIProvider api { get; set; } - [CanBeNull] - private MultiplayerRoomUser localUser; - [Resolved] private OsuColour colours { get; set; } @@ -67,14 +64,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.OnRoomUpdated(); - // this method is called on leaving the room, so the local user may not exist in the room any more. - localUser = Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); - updateState(); } private void updateState() { + var localUser = Client.LocalUser; + if (localUser == null) return; @@ -142,6 +138,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onClick() { + var localUser = Client.LocalUser; + if (localUser == null) return; From 9ff214023297f4300a439e5c2427281a0cb37bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:51:46 +0100 Subject: [PATCH 020/198] Move ready-up logic to match sub-screen --- .../Match/MultiplayerMatchFooter.cs | 9 +++++- .../Match/MultiplayerReadyButton.cs | 30 ++++--------------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 28 ++++++++++++++++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index a52f62fe00..bd1ca1377e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -19,7 +20,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); + public Action OnReady + { + set => readyButton.OnReady = value; + } + private readonly Drawable background; + private readonly MultiplayerReadyButton readyButton; public MultiplayerMatchFooter() { @@ -29,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match InternalChildren = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, - new MultiplayerReadyButton + readyButton = new MultiplayerReadyButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 9b406ff52c..96470cf070 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.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. +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.Bindables; using osu.Framework.Graphics; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Online.API; @@ -24,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; + public Action OnReady + { + set => button.Action = value; + } + [Resolved] private IAPIProvider api { get; set; } @@ -47,7 +51,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match RelativeSizeAxes = Axes.Both, Size = Vector2.One, Enabled = { Value = true }, - Action = onClick }; } @@ -136,27 +139,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } } - private void onClick() - { - var localUser = Client.LocalUser; - - if (localUser == null) - return; - - if (localUser.State == MultiplayerUserState.Idle) - Client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); - else - { - if (Room?.Host?.Equals(localUser) == true) - { - gameplayStartTracker.BeginOperation(); - Client.StartMatch().CatchUnobservedExceptions(true); - } - else - Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); - } - } - private class ButtonWithTrianglesExposed : ReadyButton { public new Triangles Triangles => base.Triangles; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 8eafd7c7df..a87471c8af 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -153,7 +154,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, new Drawable[] { - new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem } } + new MultiplayerMatchFooter + { + SelectedItem = { BindTarget = SelectedItem }, + OnReady = onReady + } } }, RowDimensions = new[] @@ -199,6 +204,27 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); + private void onReady() + { + var localUser = client.LocalUser; + + if (localUser == null) + return; + + if (localUser.State == MultiplayerUserState.Idle) + client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); + else + { + if (client.Room?.Host?.Equals(localUser) == true) + { + gameplayStartTracker.BeginOperation(); + client.StartMatch().CatchUnobservedExceptions(true); + } + else + client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); + } + } + private void onLoadRequested() { Debug.Assert(client.Room != null); From f59ba799d30390f030fcac0e12ac99a1893f78f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 07:54:27 +0100 Subject: [PATCH 021/198] Adjust operation tracker implementation --- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index f2d943e14f..6a340d7954 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; namespace osu.Game.Screens.OnlinePlay @@ -24,21 +25,26 @@ namespace osu.Game.Screens.OnlinePlay /// /// Begins tracking a new online operation. /// + /// + /// An that will automatically mark the operation as ended on disposal. + /// /// An operation has already been started. - public void BeginOperation() + public IDisposable BeginOperation() { if (leasedInProgress != null) throw new InvalidOperationException("Cannot begin operation while another is in progress."); leasedInProgress = inProgress.BeginLease(true); leasedInProgress.Value = true; + + return new InvokeOnDisposal(endOperation); } /// /// Ends tracking an online operation. /// Does nothing if an operation has not been begun yet. /// - public void EndOperation() + private void endOperation() { leasedInProgress?.Return(); leasedInProgress = null; From db52255bbec7a284b07baac967280e2cf5b3d9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 08:20:43 +0100 Subject: [PATCH 022/198] Adjust tracker usages to match new API --- .../TestSceneCreateMultiplayerMatchButton.cs | 9 ++++-- .../TestSceneMultiplayerReadyButton.cs | 9 ++++-- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 21 +++++++++---- .../CreateMultiplayerMatchButton.cs | 10 +++--- .../Match/MultiplayerMatchSettingsOverlay.cs | 31 +++++++++++++++---- .../Match/MultiplayerReadyButton.cs | 10 +++--- .../Multiplayer/MultiplayerMatchSubScreen.cs | 19 +++++++++--- .../Screens/OnlinePlay/OnlinePlayScreen.cs | 2 +- 8 files changed, 79 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs index 2a549e5262..381270c5aa 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -12,7 +13,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene { [Cached] - private OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); private CreateMultiplayerMatchButton button; @@ -31,12 +32,14 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestButtonEnableStateChanges() { + IDisposable joiningRoomOperation = null; + assertButtonEnableState(true); - AddStep("begin joining room", () => joiningRoomTracker.BeginOperation()); + AddStep("begin joining room", () => joiningRoomOperation = ongoingOperationTracker.BeginOperation()); assertButtonEnableState(false); - AddStep("end joining room", () => joiningRoomTracker.EndOperation()); + AddStep("end joining room", () => joiningRoomOperation.Dispose()); assertButtonEnableState(true); AddStep("disconnect client", () => Client.Disconnect()); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 958c6d218b..de02c6c844 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -31,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; [Cached] - private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -167,11 +168,15 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { + IDisposable gameplayStartOperation = null; + + AddStep("hook up tracker", () => button.OnReady = () => gameplayStartOperation = ongoingOperationTracker.BeginOperation()); + addClickButtonStep(); AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => gameplayStartTracker.EndOperation()); + AddStep("transitioned to gameplay", () => gameplayStartOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index f25835f56a..2a16a62714 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -1,7 +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.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -39,7 +42,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private MusicController music { get; set; } [Resolved(CanBeNull = true)] - private OngoingOperationTracker joiningRoomTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + [CanBeNull] + private IDisposable joiningRoomOperation { get; set; } [BackgroundDependencyLoader] private void load() @@ -102,9 +108,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer()); - if (joiningRoomTracker != null) + if (ongoingOperationTracker != null) { - joiningRoom.BindTo(joiningRoomTracker.InProgress); + joiningRoom.BindTo(ongoingOperationTracker.InProgress); joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); } } @@ -164,15 +170,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void joinRequested(Room room) { - joiningRoomTracker?.BeginOperation(); + Debug.Assert(joiningRoomOperation == null); + joiningRoomOperation = ongoingOperationTracker?.BeginOperation(); RoomManager?.JoinRoom(room, r => { Open(room); - joiningRoomTracker?.EndOperation(); + joiningRoomOperation?.Dispose(); + joiningRoomOperation = null; }, _ => { - joiningRoomTracker?.EndOperation(); + joiningRoomOperation?.Dispose(); + joiningRoomOperation = null; }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 7518a4e71b..3785dfc29f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -11,13 +11,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public class CreateMultiplayerMatchButton : PurpleTriangleButton { private IBindable isConnected; - private IBindable joiningRoom; + private IBindable operationInProgress; [Resolved] private StatefulMultiplayerClient multiplayerClient { get; set; } [Resolved] - private OngoingOperationTracker joiningRoomTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } [BackgroundDependencyLoader] private void load() @@ -29,10 +29,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer isConnected = multiplayerClient.IsConnected.GetBoundCopy(); isConnected.BindValueChanged(_ => updateState()); - joiningRoom = joiningRoomTracker.InProgress.GetBoundCopy(); - joiningRoom.BindValueChanged(_ => updateState(), true); + operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + operationInProgress.BindValueChanged(_ => updateState(), true); } - private void updateState() => Enabled.Value = isConnected.Value && !joiningRoom.Value; + private void updateState() => Enabled.Value = isConnected.Value && !operationInProgress.Value; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index de25cff546..59b138123d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -68,7 +70,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private Bindable ruleset { get; set; } - private readonly OngoingOperationTracker applyingSettingsTracker = new OngoingOperationTracker(); + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } + + private readonly IBindable operationInProgress = new BindableBool(); + + [CanBeNull] + private IDisposable applyingSettingsOperation; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -277,7 +285,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true); RoomID.BindValueChanged(roomId => initialBeatmapControl.Alpha = roomId.NewValue == null ? 1 : 0, true); - applyingSettingsTracker.InProgress.BindValueChanged(v => + operationInProgress.BindTo(ongoingOperationTracker.InProgress); + operationInProgress.BindValueChanged(v => { if (v.NewValue) loadingLayer.Show(); @@ -290,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { base.Update(); - ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !applyingSettingsTracker.InProgress.Value; + ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value; } private void apply() @@ -299,7 +308,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match return; hideError(); - applyingSettingsTracker.BeginOperation(); + + Debug.Assert(applyingSettingsOperation == null); + applyingSettingsOperation = ongoingOperationTracker.BeginOperation(); // 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. @@ -332,15 +343,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private void onSuccess(Room room) { - applyingSettingsTracker.EndOperation(); + Debug.Assert(applyingSettingsOperation != null); + SettingsApplied?.Invoke(); + + applyingSettingsOperation.Dispose(); + applyingSettingsOperation = null; } private void onError(string text) { + Debug.Assert(applyingSettingsOperation != null); + ErrorText.Text = text; ErrorText.FadeIn(50); - applyingSettingsTracker.EndOperation(); + + applyingSettingsOperation.Dispose(); + applyingSettingsOperation = null; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 96470cf070..b145cd46c1 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -35,14 +35,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match private OsuColour colours { get; set; } [Resolved] - private OngoingOperationTracker gameplayStartTracker { get; set; } + private OngoingOperationTracker ongoingOperationTracker { get; set; } private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; - private IBindable gameplayStartInProgress; + private IBindable operationInProgress; public MultiplayerReadyButton() { @@ -59,8 +59,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { sampleReadyCount = audio.Samples.Get(@"SongSelect/select-difficulty"); - gameplayStartInProgress = gameplayStartTracker.InProgress.GetBoundCopy(); - gameplayStartInProgress.BindValueChanged(_ => updateState()); + operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + operationInProgress.BindValueChanged(_ => updateState()); } protected override void OnRoomUpdated() @@ -105,7 +105,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match break; } - button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !gameplayStartInProgress.Value; + button.Enabled.Value = Client.Room?.State == MultiplayerRoomState.Open && !operationInProgress.Value; if (newCountReady != countReady) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index a87471c8af..b82bf508ce 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -32,13 +34,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } - [Cached] - private OngoingOperationTracker gameplayStartTracker = new OngoingOperationTracker(); + [Resolved] + private OngoingOperationTracker ongoingOperationTracker { get; set; } private MultiplayerMatchSettingsOverlay settingsOverlay; private IBindable isConnected; + [CanBeNull] + private IDisposable gameplayStartOperation; + public MultiplayerMatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name.Value; @@ -217,7 +222,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { if (client.Room?.Host?.Equals(localUser) == true) { - gameplayStartTracker.BeginOperation(); + Debug.Assert(gameplayStartOperation == null); + gameplayStartOperation = ongoingOperationTracker.BeginOperation(); + client.StartMatch().CatchUnobservedExceptions(true); } else @@ -232,7 +239,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer int[] userIds = client.CurrentMatchPlayingUserIds.ToArray(); StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - gameplayStartTracker.EndOperation(); + + Debug.Assert(gameplayStartOperation != null); + + gameplayStartOperation.Dispose(); + gameplayStartOperation = null; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index 92665c498b..13ebc1912e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private readonly OngoingOperationTracker joiningRoomTracker = new OngoingOperationTracker(); + private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); [Resolved(CanBeNull = true)] private MusicController music { get; set; } From e9b0652359e9bcffed8c4e84f95a35f3397eef45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:09:47 +0100 Subject: [PATCH 023/198] Move ready-up operation logic again to client To salvage ready up button tests. --- .../TestSceneMultiplayerReadyButton.cs | 16 ++++++--- .../Multiplayer/StatefulMultiplayerClient.cs | 33 +++++++++++++++++++ .../Match/MultiplayerMatchFooter.cs | 4 +-- .../Match/MultiplayerReadyButton.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 33 ++++++++----------- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index de02c6c844..86398b205d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + private IDisposable toggleReadyOperation; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -61,6 +63,14 @@ namespace osu.Game.Tests.Visual.Multiplayer Beatmap = { Value = beatmap }, Ruleset = { Value = beatmap.Ruleset } } + }, + OnToggleReady = async () => + { + toggleReadyOperation = ongoingOperationTracker.BeginOperation(); + + bool gameplayStarted = await Client.ToggleReady(); + if (!gameplayStarted) + toggleReadyOperation.Dispose(); } }; }); @@ -168,15 +178,11 @@ namespace osu.Game.Tests.Visual.Multiplayer private void verifyGameplayStartFlow() { - IDisposable gameplayStartOperation = null; - - AddStep("hook up tracker", () => button.OnReady = () => gameplayStartOperation = ongoingOperationTracker.BeginOperation()); - addClickButtonStep(); AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => gameplayStartOperation.Dispose()); + AddStep("transitioned to gameplay", () => toggleReadyOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 0f44085134..ee42e56d75 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -183,6 +183,39 @@ namespace osu.Game.Online.Multiplayer }); } + /// + /// Toggles the 's ready state. + /// + /// true if this toggle triggered a gameplay start; false otherwise. + /// If a toggle of ready state is not valid at this time. + public async Task ToggleReady() + { + var localUser = LocalUser; + + if (localUser == null) + return false; + + switch (localUser.State) + { + case MultiplayerUserState.Idle: + await ChangeState(MultiplayerUserState.Ready); + return false; + + case MultiplayerUserState.Ready: + if (Room?.Host?.Equals(localUser) == true) + { + await StartMatch(); + return true; + } + + await ChangeState(MultiplayerUserState.Idle); + return false; + + default: + throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); + } + } + public abstract Task TransferHost(int userId); public abstract Task ChangeSettings(MultiplayerRoomSettings settings); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index bd1ca1377e..a5e7650e31 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); - public Action OnReady + public Action OnToggleReady { - set => readyButton.OnReady = value; + set => readyButton.OnToggleReady = value; } 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 b145cd46c1..251b5b30ae 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; - public Action OnReady + public Action OnToggleReady { set => button.Action = value; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index b82bf508ce..88b4c8a1bf 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnReady = onReady + OnToggleReady = onToggleReady } } }, @@ -209,27 +209,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); - private void onReady() + private void onToggleReady() { - var localUser = client.LocalUser; + Debug.Assert(gameplayStartOperation == null); + gameplayStartOperation = ongoingOperationTracker.BeginOperation(); - if (localUser == null) - return; + client.ToggleReady() + .ContinueWith(t => + { + // if gameplay was started, the button will be unblocked on load requested. + if (t.Result) return; - if (localUser.State == MultiplayerUserState.Idle) - client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true); - else - { - if (client.Room?.Host?.Equals(localUser) == true) - { - Debug.Assert(gameplayStartOperation == null); - gameplayStartOperation = ongoingOperationTracker.BeginOperation(); - - client.StartMatch().CatchUnobservedExceptions(true); - } - else - client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); - } + // gameplay was not started; unblock button. + gameplayStartOperation?.Dispose(); + gameplayStartOperation = null; + }) + .CatchUnobservedExceptions(); } private void onLoadRequested() From 274730de34336b646ca57027c6820922cd9fc0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:15:33 +0100 Subject: [PATCH 024/198] Cache tracker in test scene to resolve test fails --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 8869718fd1..2344ebea0e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -3,10 +3,12 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Beatmaps; @@ -18,6 +20,9 @@ namespace osu.Game.Tests.Visual.Multiplayer { private MultiplayerMatchSubScreen screen; + [Cached] + private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + public TestSceneMultiplayerMatchSubScreen() : base(false) { From cafa241ef3e7d73952956fda2ec83b04ef519da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 09:41:32 +0100 Subject: [PATCH 025/198] Fix ready-up button getting stuck if server operation fails --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 88b4c8a1bf..5676bb22b4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -11,7 +11,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; -using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -218,13 +217,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer .ContinueWith(t => { // if gameplay was started, the button will be unblocked on load requested. - if (t.Result) return; + // accessing Exception here also silences any potential errors from the antecedent task + // (we still want to unblock the button if the ready-up fails). + if (t.Exception == null && t.Result) return; // gameplay was not started; unblock button. gameplayStartOperation?.Dispose(); gameplayStartOperation = null; - }) - .CatchUnobservedExceptions(); + }); } private void onLoadRequested() From 59f2017a13672be09b527873470d528717d4ae70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:22:11 +0100 Subject: [PATCH 026/198] Move BindValueChanged subscriptions to LoadComplete --- .../Multiplayer/CreateMultiplayerMatchButton.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 3785dfc29f..87b0e49b5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -27,9 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Text = "Create room"; isConnected = multiplayerClient.IsConnected.GetBoundCopy(); - isConnected.BindValueChanged(_ => updateState()); - operationInProgress = ongoingOperationTracker.InProgress.GetBoundCopy(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + isConnected.BindValueChanged(_ => updateState()); operationInProgress.BindValueChanged(_ => updateState(), true); } From dd87478690096232e4833dc69116006b1bf1e57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:29:19 +0100 Subject: [PATCH 027/198] Add helper IsHost property to Client --- .../Online/Multiplayer/StatefulMultiplayerClient.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index ee42e56d75..290765dc35 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -70,6 +70,18 @@ namespace osu.Game.Online.Multiplayer /// public MultiplayerRoomUser? LocalUser => Room?.Users.SingleOrDefault(u => u.User?.Id == api.LocalUser.Value.Id); + /// + /// Whether the is the host in . + /// + public bool IsHost + { + get + { + var localUser = LocalUser; + return localUser != null && Room?.Host != null && localUser.Equals(Room.Host); + } + } + [Resolved] private UserLookupCache userLookupCache { get; set; } = null!; From d34609b98ed925ccb181f2fc6d47101e3c1623a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 16:29:36 +0100 Subject: [PATCH 028/198] Rename On{ToggleReady -> ReadyClick} --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 2 +- .../OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs | 4 ++-- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 2 +- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 86398b205d..1d82b29a91 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = beatmap.Ruleset } } }, - OnToggleReady = async () => + OnReadyClick = async () => { toggleReadyOperation = ongoingOperationTracker.BeginOperation(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs index a5e7650e31..bbf861fac3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchFooter.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match public readonly Bindable SelectedItem = new Bindable(); - public Action OnToggleReady + public Action OnReadyClick { - set => readyButton.OnToggleReady = value; + set => readyButton.OnReadyClick = value; } 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 251b5b30ae..c6018a79e9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public Bindable SelectedItem => button.SelectedItem; - public Action OnToggleReady + public Action OnReadyClick { set => button.Action = value; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 5676bb22b4..b00ab065fb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -161,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnToggleReady = onToggleReady + OnReadyClick = onToggleReady } } }, From f800448c87f163346a2ce4284ace782646816823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 30 Dec 2020 18:00:57 +0100 Subject: [PATCH 029/198] Move game start logic to a higher level --- .../TestSceneMultiplayerReadyButton.cs | 17 ++++--- .../Multiplayer/StatefulMultiplayerClient.cs | 15 ++---- .../Multiplayer/MultiplayerMatchSubScreen.cs | 51 ++++++++++++------- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 1d82b29a91..a6037dcbf2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private IDisposable toggleReadyOperation; + private IDisposable readyClickOperation; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -66,11 +66,16 @@ namespace osu.Game.Tests.Visual.Multiplayer }, OnReadyClick = async () => { - toggleReadyOperation = ongoingOperationTracker.BeginOperation(); + readyClickOperation = ongoingOperationTracker.BeginOperation(); - bool gameplayStarted = await Client.ToggleReady(); - if (!gameplayStarted) - toggleReadyOperation.Dispose(); + if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready) + { + await Client.StartMatch(); + return; + } + + await Client.ToggleReady(); + readyClickOperation.Dispose(); } }; }); @@ -182,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("user waiting for load", () => Client.Room?.Users[0].State == MultiplayerUserState.WaitingForLoad); AddAssert("ready button disabled", () => !button.ChildrenOfType().Single().Enabled.Value); - AddStep("transitioned to gameplay", () => toggleReadyOperation.Dispose()); + AddStep("transitioned to gameplay", () => readyClickOperation.Dispose()); AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 290765dc35..5c6baa4fea 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -198,30 +198,23 @@ namespace osu.Game.Online.Multiplayer /// /// Toggles the 's ready state. /// - /// true if this toggle triggered a gameplay start; false otherwise. /// If a toggle of ready state is not valid at this time. - public async Task ToggleReady() + public async Task ToggleReady() { var localUser = LocalUser; if (localUser == null) - return false; + return; switch (localUser.State) { case MultiplayerUserState.Idle: await ChangeState(MultiplayerUserState.Ready); - return false; + return; case MultiplayerUserState.Ready: - if (Room?.Host?.Equals(localUser) == true) - { - await StartMatch(); - return true; - } - await ChangeState(MultiplayerUserState.Idle); - return false; + return; default: throw new InvalidOperationException($"Cannot toggle ready when in {localUser.State}"); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index b00ab065fb..1bef8841f5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; +using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; @@ -41,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private IBindable isConnected; [CanBeNull] - private IDisposable gameplayStartOperation; + private IDisposable readyClickOperation; public MultiplayerMatchSubScreen(Room room) { @@ -161,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new MultiplayerMatchFooter { SelectedItem = { BindTarget = SelectedItem }, - OnReadyClick = onToggleReady + OnReadyClick = onReadyClick } } }, @@ -208,23 +209,37 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); - private void onToggleReady() + private void onReadyClick() { - Debug.Assert(gameplayStartOperation == null); - gameplayStartOperation = ongoingOperationTracker.BeginOperation(); + Debug.Assert(readyClickOperation == null); + readyClickOperation = ongoingOperationTracker.BeginOperation(); + + if (client.IsHost && client.LocalUser?.State == MultiplayerUserState.Ready) + { + client.StartMatch() + .ContinueWith(t => + { + // accessing Exception here silences any potential errors from the antecedent task + if (t.Exception != null) + { + // gameplay was not started due to an exception; unblock button. + endOperation(); + } + + // gameplay is starting, the button will be unblocked on load requested. + }); + return; + } client.ToggleReady() - .ContinueWith(t => - { - // if gameplay was started, the button will be unblocked on load requested. - // accessing Exception here also silences any potential errors from the antecedent task - // (we still want to unblock the button if the ready-up fails). - if (t.Exception == null && t.Result) return; + .ContinueWith(_ => endOperation()) + .CatchUnobservedExceptions(); - // gameplay was not started; unblock button. - gameplayStartOperation?.Dispose(); - gameplayStartOperation = null; - }); + void endOperation() + { + readyClickOperation?.Dispose(); + readyClickOperation = null; + } } private void onLoadRequested() @@ -235,10 +250,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - Debug.Assert(gameplayStartOperation != null); + Debug.Assert(readyClickOperation != null); - gameplayStartOperation.Dispose(); - gameplayStartOperation = null; + readyClickOperation.Dispose(); + readyClickOperation = null; } protected override void Dispose(bool isDisposing) From 7d9a61fbc1b7afad59df89d3cf13cea4cce8be8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 31 Dec 2020 11:50:59 +0100 Subject: [PATCH 030/198] Handle unobserved exceptions from ready button properly --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 1bef8841f5..39323d9db9 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -222,6 +222,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // accessing Exception here silences any potential errors from the antecedent task if (t.Exception != null) { + t.CatchUnobservedExceptions(true); // will run immediately. // gameplay was not started due to an exception; unblock button. endOperation(); } @@ -232,8 +233,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } client.ToggleReady() - .ContinueWith(_ => endOperation()) - .CatchUnobservedExceptions(); + .ContinueWith(t => + { + t.CatchUnobservedExceptions(true); // will run immediately. + endOperation(); + }); void endOperation() { From b4df2d6d43f96659c6687badc4c739770957ebfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:46:09 +0300 Subject: [PATCH 031/198] Add method for copying properties from another mod --- osu.Game/Rulesets/Mods/Mod.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..236bf9ff00 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,22 +131,31 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var copy = (Mod)Activator.CreateInstance(GetType()); + var result = (Mod)Activator.CreateInstance(GetType(), true); + result.CopyFrom(this); + return result; + } + + /// + /// Copies properties of given mod into here, through changing value. + /// + /// The mod to copy properties from. + public void CopyFrom(Mod them) + { + if (them.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); - // Copy bindable values across foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = prop.GetValue(this); - var copyBindable = prop.GetValue(copy); + var ourBindable = prop.GetValue(this); + var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); - valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable)); + valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); } - - return copy; } public bool Equals(IMod other) => GetType() == other?.GetType(); From 2ce9599957d50f5c9b8220cf7d3105372191a6d4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:13 +0300 Subject: [PATCH 032/198] Copy selected mods properties into overlay's buttons --- osu.Game/Overlays/Mods/ModSection.cs | 24 +++++++++++++++------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0107f94dcf..86da179064 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -127,18 +127,28 @@ namespace osu.Game.Overlays.Mods } /// - /// Select one or more mods in this section and deselects all other ones. + /// Updates all buttons with the given list of selected mods. /// - /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) + /// The types of s to select. + public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t == m.GetType())); + int index = -1; - if (i >= 0) - button.SelectAt(i); - else + foreach (var mod in newSelectedMods) + { + index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); + if (index < 0) + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + break; + } + + if (index < 0) button.Deselect(); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 34f5c70adb..c1622548f5 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -409,7 +409,7 @@ namespace osu.Game.Overlays.Mods private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) - section.SelectTypes(mods.NewValue.Select(m => m.GetType()).ToList()); + section.UpdateSelectedMods(mods.NewValue); updateMods(); } From ee664ad57117fbdd01dd38a3025a621e69bf7eca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:18 +0300 Subject: [PATCH 033/198] Add test coverage --- .../UserInterface/TestSceneModSelectOverlay.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 6f083f4ab6..0d0acbb8f4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -131,6 +131,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); } + [Test] + public void TestExternallySetCustomizedMod() + { + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); + + AddAssert("ensure button is selected and customized accordingly", () => + { + var button = modSelect.GetModButton(SelectedMods.Value.Single()); + return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01; + }); + } + private void testSingleMod(Mod mod) { selectNext(mod); From a031c8e0b62f3933e23e049dc75633f6c4603aef Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:34:09 +0300 Subject: [PATCH 034/198] Apply documentation suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/Mods/ModSection.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 86da179064..e29ed4f5c4 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Mods /// /// Updates all buttons with the given list of selected mods. /// - /// The types of s to select. + /// The new list of selected mods to select. public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 236bf9ff00..6e83299d63 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies properties of given mod into here, through changing value. + /// Copies mod setting values from into this instance. /// /// The mod to copy properties from. public void CopyFrom(Mod them) From c1a1e3acc5aa199442ce7a96058e1eaae2bc7dd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:40:40 +0300 Subject: [PATCH 035/198] Revert drive-by changes --- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 6e83299d63..3f1f16e561 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var result = (Mod)Activator.CreateInstance(GetType(), true); + var result = (Mod)Activator.CreateInstance(GetType()); result.CopyFrom(this); return result; } @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Mods var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = ourBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); From 988f9b98a153d95b5f6de42dc3782d08912c125f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 16:16:00 +0300 Subject: [PATCH 036/198] Split button mods updating to private method --- osu.Game/Overlays/Mods/ModSection.cs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index e29ed4f5c4..573d1e5355 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -133,24 +133,24 @@ namespace osu.Game.Overlays.Mods public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) + updateButtonMods(button, newSelectedMods); + } + + private void updateButtonMods(ModButton button, IReadOnlyList newSelectedMods) + { + foreach (var mod in newSelectedMods) { - int index = -1; - - foreach (var mod in newSelectedMods) - { - index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); - if (index < 0) - continue; - - var buttonMod = button.Mods[index]; - buttonMod.CopyFrom(mod); - button.SelectAt(index); - break; - } - + var index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); if (index < 0) - button.Deselect(); + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + return; } + + button.Deselect(); } protected ModSection() From 6c3ccaddbfec4a148ad6080e2ab6e958d54cd2e0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 2 Jan 2021 12:51:41 +0000 Subject: [PATCH 037/198] Bump Microsoft.CodeAnalysis.FxCopAnalyzers from 3.3.1 to 3.3.2 Bumps [Microsoft.CodeAnalysis.FxCopAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/master/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/compare/v3.3.1...v3.3.2) Signed-off-by: dependabot-preview[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9ec442aafa..a74b204436 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,7 +18,7 @@ - + $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset From e2de5bb8f940e71fcf9cfefa8fe504966c5e00bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Jan 2021 22:05:41 +0900 Subject: [PATCH 038/198] Fix the beatmap carousel not returning to centre correctly after resizing window --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d76f0abb9e..c83c89bb7f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -567,6 +568,15 @@ namespace osu.Game.Screens.Select #endregion + protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) + { + // handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed). + if ((invalidation & Invalidation.Layout) > 0) + itemsCache.Invalidate(); + + return base.OnInvalidate(invalidation, source); + } + protected override void Update() { base.Update(); From 26b2a065bc365fc75e04315dfe9cbc71a3b0bb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 2 Jan 2021 14:25:15 +0100 Subject: [PATCH 039/198] Add deprecation warning ignore with explanation --- Directory.Build.props | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a74b204436..b55eff9df9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,9 +28,17 @@ $(NoWarn);CS1591 - - $(NoWarn);NU1701 + + $(NoWarn);NU1701;CA9998 false @@ -43,4 +51,4 @@ Copyright (c) 2020 ppy Pty Ltd osu game - \ No newline at end of file + From 8bb84570df904c9c55cc781e18cc13bfb13e834e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:21:50 +0300 Subject: [PATCH 040/198] Introduce beatmap availability structure --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 osu.Game/Online/Rooms/BeatmapAvailability.cs diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs new file mode 100644 index 0000000000..ca53cb2295 --- /dev/null +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Online.Rooms +{ + /// + /// The local availability information about a certain beatmap for the client. + /// + public readonly struct BeatmapAvailability : IEquatable + { + /// + /// The beatmap's availability state. + /// + public readonly DownloadState State; + + /// + /// The beatmap's downloading progress, null when not in state. + /// + public readonly double? DownloadProgress; + + /// + /// Constructs a new non- beatmap availability state. + /// + /// The beatmap availability state. + /// Throws if was specified in this constructor, as it has its own constructor (see . + public BeatmapAvailability(DownloadState state) + { + if (state == DownloadState.Downloading) + throw new ArgumentException($"{nameof(DownloadState.Downloading)} state has its own constructor, use it instead."); + + State = state; + DownloadProgress = null; + } + + /// + /// Constructs a new -specific beatmap availability state. + /// + /// The beatmap availability state (always ). + /// The beatmap's downloading current progress. + /// Throws if non- was specified in this constructor, as they have their own constructor (see . + public BeatmapAvailability(DownloadState state, double downloadProgress) + { + if (state != DownloadState.Downloading) + throw new ArgumentException($"This is a constructor specific for {DownloadState.Downloading} state, use the regular one instead."); + + State = DownloadState.Downloading; + DownloadProgress = downloadProgress; + } + + public bool Equals(BeatmapAvailability other) => State == other.State && DownloadProgress == other.DownloadProgress; + + public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; + } +} From 09e5e2629ab2ffa0ad052683bcb91000e18a87fe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:25:06 +0300 Subject: [PATCH 041/198] Add user beatmap availability property --- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 99624dc3e7..92c92f438d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -5,6 +5,7 @@ using System; using Newtonsoft.Json; +using osu.Game.Online.Rooms; using osu.Game.Users; namespace osu.Game.Online.Multiplayer @@ -16,6 +17,11 @@ namespace osu.Game.Online.Multiplayer public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; + /// + /// The availability state of the beatmap, set to by default. + /// + public BeatmapAvailability BeatmapAvailability { get; set; } = new BeatmapAvailability(DownloadState.LocallyAvailable); + public User? User { get; set; } [JsonConstructor] From dfa8be9173839c3f5dd7aaabb14f0a60692580f1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 04:32:50 +0300 Subject: [PATCH 042/198] Add beatmap availability change state & event methods --- .../Online/Multiplayer/IMultiplayerClient.cs | 8 ++++++++ .../Multiplayer/IMultiplayerRoomServer.cs | 8 ++++++++ .../Online/Multiplayer/MultiplayerClient.cs | 9 +++++++++ .../Multiplayer/StatefulMultiplayerClient.cs | 20 +++++++++++++++++++ .../Multiplayer/TestMultiplayerClient.cs | 15 ++++++++++++++ 5 files changed, 60 insertions(+) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index b97fcc9ae7..5410fbc030 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -47,6 +48,13 @@ namespace osu.Game.Online.Multiplayer /// The new state of the user. Task UserStateChanged(int userId, MultiplayerUserState state); + /// + /// Signals that a user in this room has their beatmap availability state changed. + /// + /// The ID of the user whose beatmap availability state has changed. + /// The new beatmap availability state of the user. + Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability); + /// /// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point. /// diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 481e3fb1de..7fda526faf 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -40,6 +41,13 @@ namespace osu.Game.Online.Multiplayer /// If the user is not in a room. Task ChangeState(MultiplayerUserState newState); + /// + /// Change the user's local availability state of the beatmap set in joined room. + /// This will also force user state back to . + /// + /// The proposed new beatmap availability state. + Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + /// /// As the host of a room, start the match. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 7cd1ef78f7..50dc8f661c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API; +using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer { @@ -173,6 +174,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeState), newState); } + public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability); + } + public override Task StartMatch() { if (!isConnected.Value) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index dc80488d39..c1818de87a 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -184,6 +184,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeState(MultiplayerUserState newState); + public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + public abstract Task StartMatch(); Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) @@ -311,6 +313,24 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + Task IMultiplayerClient.UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability) + { + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + if (Room == null) + return; + + Room.Users.Single(u => u.UserID == userId).BeatmapAvailability = beatmapAvailability; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; + } + Task IMultiplayerClient.LoadRequested() { if (Room == null) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 2ce5211757..c155447f8c 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Online.Rooms; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer @@ -77,6 +78,14 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } + public void ChangeUserBeatmapAvailability(int userId, BeatmapAvailability newBeatmapAvailability) + { + Debug.Assert(Room != null); + + ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); + ChangeUserState(userId, MultiplayerUserState.Idle); + } + protected override Task JoinRoom(long roomId) { var user = new MultiplayerRoomUser(api.LocalUser.Value.Id) { User = api.LocalUser.Value }; @@ -108,6 +117,12 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } + public override Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability) + { + ChangeUserBeatmapAvailability(api.LocalUser.Value.Id, newBeatmapAvailability); + return Task.CompletedTask; + } + public override Task StartMatch() { Debug.Assert(Room != null); From caa5109e3a0e1e526c789024d2ecbc9d57767e76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 12:18:35 +0900 Subject: [PATCH 043/198] Add precautionary null checks to update methods in SongSelect --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a5252fdc96..8ad1ace36a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -428,7 +428,7 @@ namespace osu.Game.Screens.Select private void updateSelectedBeatmap(BeatmapInfo beatmap) { - if (beatmap?.Equals(beatmapNoDebounce) == true) + if (beatmap == null || beatmap.Equals(beatmapNoDebounce)) return; beatmapNoDebounce = beatmap; @@ -438,7 +438,7 @@ namespace osu.Game.Screens.Select private void updateSelectedRuleset(RulesetInfo ruleset) { - if (ruleset?.Equals(rulesetNoDebounce) == true) + if (ruleset == null || ruleset.Equals(rulesetNoDebounce)) return; rulesetNoDebounce = ruleset; From 2e5c67be3f415bc0841c4ae2cd122237ff49c380 Mon Sep 17 00:00:00 2001 From: LavaDesu Date: Wed, 30 Dec 2020 12:29:51 +0700 Subject: [PATCH 044/198] Add ability to toggle discord rich presence There are 3 modes: enabled, limited, and disabled. The limited mode hides identifiable information such as username, rank, and (if participating in one) multiplayer lobby name. --- osu.Desktop/DiscordRichPresence.cs | 15 ++++++++--- .../Configuration/DiscordRichPresenceMode.cs | 17 ++++++++++++ osu.Game/Configuration/OsuConfigManager.cs | 5 +++- .../Sections/Online/IntegrationSettings.cs | 27 +++++++++++++++++++ .../Settings/Sections/OnlineSection.cs | 3 ++- 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Configuration/DiscordRichPresenceMode.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index f1878d967d..6b331d4952 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; +using osu.Game.Configuration; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Users; @@ -30,6 +31,7 @@ namespace osu.Desktop private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); + private readonly Bindable mode = new Bindable(); private readonly RichPresence presence = new RichPresence { @@ -37,7 +39,7 @@ namespace osu.Desktop }; [BackgroundDependencyLoader] - private void load(IAPIProvider provider) + private void load(IAPIProvider provider, OsuConfigManager config) { client = new DiscordRpcClient(client_id) { @@ -51,6 +53,7 @@ namespace osu.Desktop client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); + config.BindWith(OsuSetting.DiscordRichPresence, mode); (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => { status.UnbindBindings(); @@ -63,6 +66,7 @@ namespace osu.Desktop ruleset.BindValueChanged(_ => updateStatus()); status.BindValueChanged(_ => updateStatus()); activity.BindValueChanged(_ => updateStatus()); + mode.BindValueChanged(_ => updateStatus()); client.Initialize(); } @@ -78,7 +82,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline) + if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Disabled) { client.ClearPresence(); return; @@ -96,7 +100,10 @@ namespace osu.Desktop } // update user information - presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); + if (mode.Value == DiscordRichPresenceMode.Limited) + presence.Assets.LargeImageText = string.Empty; + else + presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); // update ruleset presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom"; @@ -137,7 +144,7 @@ namespace osu.Desktop return edit.Beatmap.ToString(); case UserActivity.InLobby lobby: - return lobby.Room.Name.Value; + return mode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; } return string.Empty; diff --git a/osu.Game/Configuration/DiscordRichPresenceMode.cs b/osu.Game/Configuration/DiscordRichPresenceMode.cs new file mode 100644 index 0000000000..bd39faff4d --- /dev/null +++ b/osu.Game/Configuration/DiscordRichPresenceMode.cs @@ -0,0 +1,17 @@ +// 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; + +namespace osu.Game.Configuration +{ + public enum DiscordRichPresenceMode + { + Disabled, + + [Description("Hide identifiable information")] + Limited, + + Enabled + } +} diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index a07e446d2e..c733fe2fb4 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -138,6 +138,8 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); + Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Enabled); + Set(OsuSetting.EditorWaveformOpacity, 1f); } @@ -266,6 +268,7 @@ namespace osu.Game.Configuration GameplayDisableWinKey, SeasonalBackgroundMode, EditorWaveformOpacity, + DiscordRichPresence, AutomaticallyDownloadWhenSpectating, } } diff --git a/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs new file mode 100644 index 0000000000..d2867962c0 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Online/IntegrationSettings.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Online +{ + public class IntegrationSettings : SettingsSubsection + { + protected override string Header => "Integrations"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new Drawable[] + { + new SettingsEnumDropdown + { + LabelText = "Discord Rich Presence", + Current = config.GetBindable(OsuSetting.DiscordRichPresence) + } + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index 150cddb388..7aa4eff29a 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -20,7 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections { Children = new Drawable[] { - new WebSettings() + new WebSettings(), + new IntegrationSettings() }; } } From a6d49929978a2617ef2f180d3e3e868513e8737e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 12:53:25 +0900 Subject: [PATCH 045/198] Ensure SelectionChanged events are only sent once when selection is null --- osu.Game/Screens/Select/BeatmapCarousel.cs | 31 ++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c83c89bb7f..37213c6003 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -125,6 +125,8 @@ namespace osu.Game.Screens.Select { BeatmapSetsChanged?.Invoke(); BeatmapSetsLoaded = true; + + itemsCache.Invalidate(); }); } @@ -731,6 +733,12 @@ namespace osu.Game.Screens.Select private const float panel_padding = 5; + /// + /// After loading, we want to invoke a selection changed event at least once. + /// This handles the case where this event is potentially sending a null selection. + /// + private bool sentInitialSelectionEvent; + /// /// Computes the target Y positions for every item in the carousel. /// @@ -787,13 +795,21 @@ namespace osu.Game.Screens.Select Scroll.ScrollContent.Height = currentY; - if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) - { - selectedBeatmapSet = null; - SelectionChanged?.Invoke(null); - } - itemsCache.Validate(); + + // update and let external consumers know about selection loss. + if (BeatmapSetsLoaded) + { + bool selectionLost = selectedBeatmapSet != null && selectedBeatmapSet.State.Value != CarouselItemState.Selected; + + if (selectionLost || !sentInitialSelectionEvent) + { + selectedBeatmapSet = null; + SelectionChanged?.Invoke(null); + + sentInitialSelectionEvent = true; + } + } } private bool firstScroll = true; @@ -816,14 +832,13 @@ namespace osu.Game.Screens.Select break; case PendingScrollOperation.Immediate: + // in order to simplify animation logic, rather than using the animated version of ScrollTo, // we take the difference in scroll height and apply to all visible panels. // this avoids edge cases like when the visible panels is reduced suddenly, causing ScrollContainer // to enter clamp-special-case mode where it animates completely differently to normal. float scrollChange = scrollTarget.Value - Scroll.Current; - Scroll.ScrollTo(scrollTarget.Value, false); - foreach (var i in Scroll.Children) i.Y += scrollChange; break; From 1a443381247492b9233ae9c01ebf1a44510359ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:38:28 +0900 Subject: [PATCH 046/198] Use SingleOrDefault for added safety when looking up mod acronyms --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 780e5daa16..6c988e2e00 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -36,7 +36,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); + Mod resultMod = ruleset.GetAllMods().SingleOrDefault(m => m.Acronym == Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); From 23e216fa0b374e23751c97124dc069877004a60d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:47:15 +0900 Subject: [PATCH 047/198] Simplify some default value checks (we are sure the return is an IBindable) --- osu.Game/Online/API/APIMod.cs | 7 ++++++- osu.Game/Rulesets/Mods/Mod.cs | 8 +++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 6c988e2e00..a7e5f0d6f9 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -31,7 +31,12 @@ namespace osu.Game.Online.API Acronym = mod.Acronym; foreach (var (_, property) in mod.GetSettingsSourceProperties()) - Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + { + var bindable = (IBindable)property.GetValue(mod); + + if (!bindable.IsDefault) + Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + } } public Mod ToMod(Ruleset ruleset) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..92b548a9cc 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -84,12 +84,10 @@ namespace osu.Game.Rulesets.Mods foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties()) { - object bindableObj = property.GetValue(this); + var bindable = (IBindable)property.GetValue(this); - if ((bindableObj as IHasDefaultValue)?.IsDefault == true) - continue; - - tooltipTexts.Add($"{attr.Label} {bindableObj}"); + if (!bindable.IsDefault) + tooltipTexts.Add($"{attr.Label} {bindable}"); } return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s))); From 29dbb1cc0d94d26b3fb077e924bd2eb6d6b16dfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 15:48:28 +0900 Subject: [PATCH 048/198] Add internal pathway for ensuring correct application of bindable mods --- osu.Game/Online/API/APIMod.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 21 ++++++++++++------- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 6 ++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index a7e5f0d6f9..6e3bdac61a 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -51,7 +51,7 @@ namespace osu.Game.Online.API if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) continue; - ((IBindable)property.GetValue(resultMod)).Parse(settingValue); + resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); } return resultMod; diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 92b548a9cc..487cdedd13 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; using Newtonsoft.Json; @@ -134,19 +133,25 @@ namespace osu.Game.Rulesets.Mods // Copy bindable values across foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = prop.GetValue(this); - var copyBindable = prop.GetValue(copy); + var origBindable = (IBindable)prop.GetValue(this); + var copyBindable = (IBindable)prop.GetValue(copy); - // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); - Debug.Assert(valueProperty != null); - - valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable)); + // we only care about changes that have been made away from defaults. + if (!origBindable.IsDefault) + copy.CopyAdjustedSetting(copyBindable, origBindable); } return copy; } + /// + /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. + /// The base implementation will transfer the value via and should be called unless replaced with custom logic. + /// + /// The target bindable to apply the adjustment. + /// The adjustment to apply. + internal virtual void CopyAdjustedSetting(IBindable bindable, object value) => bindable.Parse(value); + public bool Equals(IMod other) => GetType() == other?.GetType(); } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 165644edbe..58af96a8df 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -114,6 +114,12 @@ namespace osu.Game.Rulesets.Mods bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; } + internal override void CopyAdjustedSetting(IBindable bindable, object value) + { + userChangedSettings[bindable] = true; + base.CopyAdjustedSetting(bindable, value); + } + /// /// Apply all custom settings to the provided beatmap. /// From 99fa0e25dcb542e98e6bdf3c71de45a891634806 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 16:46:24 +0900 Subject: [PATCH 049/198] Switch back to FirstOrDefault to allow for weird testing logic to pass --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 6e3bdac61a..3ceafc5160 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -41,7 +41,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.GetAllMods().SingleOrDefault(m => m.Acronym == Acronym); + Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym); if (resultMod == null) throw new InvalidOperationException($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); From 6ad1b7767e6ded5cd992838a7395c7205378b90c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 17:04:16 +0900 Subject: [PATCH 050/198] Update osu.Game/Online/API/APIMod.cs Co-authored-by: Salman Ahmed --- osu.Game/Online/API/APIMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 3ceafc5160..c8b76b9685 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -35,7 +35,7 @@ namespace osu.Game.Online.API var bindable = (IBindable)property.GetValue(mod); if (!bindable.IsDefault) - Settings.Add(property.Name.Underscore(), property.GetValue(mod)); + Settings.Add(property.Name.Underscore(), bindable); } } From 7c9f345cd27ff36ea5e638a53fb1c12f74712361 Mon Sep 17 00:00:00 2001 From: LavaDesu Date: Sun, 3 Jan 2021 16:46:25 +0700 Subject: [PATCH 051/198] Use better naming for DiscordRichPresenceMode --- osu.Desktop/DiscordRichPresence.cs | 2 +- osu.Game/Configuration/DiscordRichPresenceMode.cs | 4 ++-- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 6b331d4952..172db324cb 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -82,7 +82,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Disabled) + if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Off) { client.ClearPresence(); return; diff --git a/osu.Game/Configuration/DiscordRichPresenceMode.cs b/osu.Game/Configuration/DiscordRichPresenceMode.cs index bd39faff4d..2e58e3554b 100644 --- a/osu.Game/Configuration/DiscordRichPresenceMode.cs +++ b/osu.Game/Configuration/DiscordRichPresenceMode.cs @@ -7,11 +7,11 @@ namespace osu.Game.Configuration { public enum DiscordRichPresenceMode { - Disabled, + Off, [Description("Hide identifiable information")] Limited, - Enabled + Full } } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c733fe2fb4..eb34a0885d 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -138,7 +138,7 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); - Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Enabled); + Set(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); Set(OsuSetting.EditorWaveformOpacity, 1f); } From 2501707d7d86c9927fda5f3ece0d75c14e769bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 20:44:57 +0900 Subject: [PATCH 052/198] Copy values using Bind to also copy defaults --- osu.Game/Rulesets/Mods/Mod.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 487cdedd13..d2747d98c7 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -150,7 +150,17 @@ namespace osu.Game.Rulesets.Mods /// /// The target bindable to apply the adjustment. /// The adjustment to apply. - internal virtual void CopyAdjustedSetting(IBindable bindable, object value) => bindable.Parse(value); + internal virtual void CopyAdjustedSetting(IBindable bindable, object value) + { + if (value is IBindable incoming) + { + //copy including transfer of default values. + bindable.BindTo(incoming); + bindable.UnbindFrom(incoming); + } + else + bindable.Parse(value); + } public bool Equals(IMod other) => GetType() == other?.GetType(); } From a3e29b9154c996efbe051da95e9c8bbaac8c2096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Jan 2021 13:25:44 +0100 Subject: [PATCH 053/198] Rename parameters for readability --- osu.Game/Rulesets/Mods/Mod.cs | 14 +++++++------- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index d2747d98c7..b7432ec966 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -148,18 +148,18 @@ namespace osu.Game.Rulesets.Mods /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. /// The base implementation will transfer the value via and should be called unless replaced with custom logic. /// - /// The target bindable to apply the adjustment. - /// The adjustment to apply. - internal virtual void CopyAdjustedSetting(IBindable bindable, object value) + /// The target bindable to apply the adjustment. + /// The adjustment to apply. + internal virtual void CopyAdjustedSetting(IBindable target, object source) { - if (value is IBindable incoming) + if (source is IBindable sourceBindable) { //copy including transfer of default values. - bindable.BindTo(incoming); - bindable.UnbindFrom(incoming); + target.BindTo(sourceBindable); + target.UnbindFrom(sourceBindable); } else - bindable.Parse(value); + target.Parse(source); } public bool Equals(IMod other) => GetType() == other?.GetType(); diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 58af96a8df..72a4bb297f 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -114,10 +114,10 @@ namespace osu.Game.Rulesets.Mods bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault; } - internal override void CopyAdjustedSetting(IBindable bindable, object value) + internal override void CopyAdjustedSetting(IBindable target, object source) { - userChangedSettings[bindable] = true; - base.CopyAdjustedSetting(bindable, value); + userChangedSettings[target] = true; + base.CopyAdjustedSetting(target, source); } /// From 9e4a925ab1f071fced203cc081f2c6f0b7c2ca96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 3 Jan 2021 13:30:21 +0100 Subject: [PATCH 054/198] Clarify & cleanup comments some --- osu.Game/Rulesets/Mods/Mod.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b7432ec966..24d184e531 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -145,16 +145,19 @@ namespace osu.Game.Rulesets.Mods } /// - /// When creating copies or clones of a Mod, this method will be called to copy explicitly adjusted user settings. - /// The base implementation will transfer the value via and should be called unless replaced with custom logic. + /// When creating copies or clones of a Mod, this method will be called + /// to copy explicitly adjusted user settings from . + /// The base implementation will transfer the value via + /// or by binding and unbinding (if is an ) + /// and should be called unless replaced with custom logic. /// - /// The target bindable to apply the adjustment. + /// The target bindable to apply the adjustment to. /// The adjustment to apply. internal virtual void CopyAdjustedSetting(IBindable target, object source) { if (source is IBindable sourceBindable) { - //copy including transfer of default values. + // copy including transfer of default values. target.BindTo(sourceBindable); target.UnbindFrom(sourceBindable); } From efb71713efdde039ab6a3d0fce58476e6ef6d575 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 22:43:02 +0900 Subject: [PATCH 055/198] Fix null condition inhibiting deselection events --- osu.Game/Screens/Select/SongSelect.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8ad1ace36a..e3036c662b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -428,7 +428,10 @@ namespace osu.Game.Screens.Select private void updateSelectedBeatmap(BeatmapInfo beatmap) { - if (beatmap == null || beatmap.Equals(beatmapNoDebounce)) + if (beatmap == null && beatmapNoDebounce == null) + return; + + if (beatmap?.Equals(beatmapNoDebounce) == true) return; beatmapNoDebounce = beatmap; From 53e6a349bbed8492c62b66ff8b47333da8101230 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 3 Jan 2021 22:44:30 +0900 Subject: [PATCH 056/198] Fix incorrect initial conditional Turns out this wasn't actually required. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 37213c6003..36f8fbedb3 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -733,12 +733,6 @@ namespace osu.Game.Screens.Select private const float panel_padding = 5; - /// - /// After loading, we want to invoke a selection changed event at least once. - /// This handles the case where this event is potentially sending a null selection. - /// - private bool sentInitialSelectionEvent; - /// /// Computes the target Y positions for every item in the carousel. /// @@ -802,12 +796,10 @@ namespace osu.Game.Screens.Select { bool selectionLost = selectedBeatmapSet != null && selectedBeatmapSet.State.Value != CarouselItemState.Selected; - if (selectionLost || !sentInitialSelectionEvent) + if (selectionLost) { selectedBeatmapSet = null; SelectionChanged?.Invoke(null); - - sentInitialSelectionEvent = true; } } } From 152e9ecccf0f6150236d70a0d90f6d3a721e87aa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:34:11 +0300 Subject: [PATCH 057/198] Make `BeatmapAvailability` class in-line with other online data structures --- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- osu.Game/Online/Rooms/BeatmapAvailability.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 92c92f438d..f515b574df 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -20,7 +20,7 @@ namespace osu.Game.Online.Multiplayer /// /// The availability state of the beatmap, set to by default. /// - public BeatmapAvailability BeatmapAvailability { get; set; } = new BeatmapAvailability(DownloadState.LocallyAvailable); + public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); public User? User { get; set; } diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index ca53cb2295..04dcd2a84a 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Rooms /// /// The local availability information about a certain beatmap for the client. /// - public readonly struct BeatmapAvailability : IEquatable + public class BeatmapAvailability : IEquatable { /// /// The beatmap's availability state. @@ -49,7 +49,7 @@ namespace osu.Game.Online.Rooms DownloadProgress = downloadProgress; } - public bool Equals(BeatmapAvailability other) => State == other.State && DownloadProgress == other.DownloadProgress; + public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; } From c8423d1c4600d89bf4979715e7da540ca118633c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:34:58 +0300 Subject: [PATCH 058/198] Make constructors design more pleasent to eyes --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 30 ++++---------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 04dcd2a84a..1fd099fcc7 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -20,35 +20,17 @@ namespace osu.Game.Online.Rooms /// public readonly double? DownloadProgress; - /// - /// Constructs a new non- beatmap availability state. - /// - /// The beatmap availability state. - /// Throws if was specified in this constructor, as it has its own constructor (see . - public BeatmapAvailability(DownloadState state) + private BeatmapAvailability(DownloadState state, double? downloadProgress = null) { - if (state == DownloadState.Downloading) - throw new ArgumentException($"{nameof(DownloadState.Downloading)} state has its own constructor, use it instead."); - State = state; - DownloadProgress = null; - } - - /// - /// Constructs a new -specific beatmap availability state. - /// - /// The beatmap availability state (always ). - /// The beatmap's downloading current progress. - /// Throws if non- was specified in this constructor, as they have their own constructor (see . - public BeatmapAvailability(DownloadState state, double downloadProgress) - { - if (state != DownloadState.Downloading) - throw new ArgumentException($"This is a constructor specific for {DownloadState.Downloading} state, use the regular one instead."); - - State = DownloadState.Downloading; DownloadProgress = downloadProgress; } + public static BeatmapAvailability NotDownload() => new BeatmapAvailability(DownloadState.NotDownloaded); + public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); + public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); + public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; public override string ToString() => $"{string.Join(", ", State, $"{DownloadProgress:0.00%}")}"; From 839f5a75705f92cb77dfb75392ecf748a7f955d9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 3 Jan 2021 18:36:37 +0300 Subject: [PATCH 059/198] Ensure clients don't blow up when given user isn't in room --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index c1818de87a..799b66020c 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -320,10 +320,13 @@ namespace osu.Game.Online.Multiplayer Scheduler.Add(() => { - if (Room == null) + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // we don't care whether the room doesn't exist or user isn't in joined room, just return in that point. + if (user == null) return; - Room.Users.Single(u => u.UserID == userId).BeatmapAvailability = beatmapAvailability; + user.BeatmapAvailability = beatmapAvailability; RoomUpdated?.Invoke(); }, false); From df04dd21de6a16da70031ee4fac020ae0f923211 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 07:45:29 +0300 Subject: [PATCH 060/198] Add failing test case --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index c32e359de6..041df3e6ff 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -852,6 +852,39 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestItemRemovedShouldPassConsumableBeatmapSet() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) + { + try + { + var osu = LoadOsuIntoHost(host); + var manager = osu.Dependencies.Get(); + + var removedQueue = new Queue(); + manager.ItemRemoved.BindValueChanged(evt => + { + if (evt.NewValue.TryGetTarget(out var target)) + removedQueue.Enqueue(target); + }); + + var imported = await LoadOszIntoOsu(osu); + deleteBeatmapSet(imported, osu); + + Assert.That(removedQueue.Count, Is.EqualTo(1)); + + var removedItem = removedQueue.Single(); + Assert.That(removedItem.Metadata, Is.EqualTo(imported.Metadata)); + Assert.That(removedItem.Beatmaps, Is.EquivalentTo(imported.Beatmaps)); + } + finally + { + host.Exit(); + } + } + } + public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); From 738c94d1938f99ff5fb638528cff02bf80f297df Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 07:46:51 +0300 Subject: [PATCH 061/198] Update soft-deletion logic to use model store's consumable items instead --- osu.Game/Database/ArchiveModelManager.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 36cc4cce39..61b2f7668e 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -76,7 +76,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - protected readonly MutableDatabaseBackedStore ModelStore; + protected readonly MutableDatabaseBackedStoreWithFileIncludes ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; @@ -492,7 +492,7 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { // re-fetch the model on the import context. - var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID); + var foundModel = ModelStore.ConsumableItems.SingleOrDefault(i => i.ID == item.ID); if (foundModel == null || foundModel.DeletePending) return false; @@ -731,8 +731,6 @@ namespace osu.Game.Database yield return f.Filename; } - private DbSet queryModel() => ContextFactory.Get().Set(); - protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; #region Event handling / delaying From 1463ff288629232333880a0f18888c4f1a65e5d6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:12:31 +0300 Subject: [PATCH 062/198] Remove unnecessary using directive --- osu.Game/Database/ArchiveModelManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 61b2f7668e..b9f805ae31 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; From ca5f2bcd4ca8395698a5267d6a8fb2a6cb214e45 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:49:03 +0300 Subject: [PATCH 063/198] Revert database-side changes --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 ------------------- osu.Game/Database/ArchiveModelManager.cs | 6 ++-- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 041df3e6ff..c32e359de6 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -852,39 +852,6 @@ namespace osu.Game.Tests.Beatmaps.IO } } - [Test] - public async Task TestItemRemovedShouldPassConsumableBeatmapSet() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest))) - { - try - { - var osu = LoadOsuIntoHost(host); - var manager = osu.Dependencies.Get(); - - var removedQueue = new Queue(); - manager.ItemRemoved.BindValueChanged(evt => - { - if (evt.NewValue.TryGetTarget(out var target)) - removedQueue.Enqueue(target); - }); - - var imported = await LoadOszIntoOsu(osu); - deleteBeatmapSet(imported, osu); - - Assert.That(removedQueue.Count, Is.EqualTo(1)); - - var removedItem = removedQueue.Single(); - Assert.That(removedItem.Metadata, Is.EqualTo(imported.Metadata)); - Assert.That(removedItem.Beatmaps, Is.EquivalentTo(imported.Beatmaps)); - } - finally - { - host.Exit(); - } - } - } - public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index b9f805ae31..81d8668196 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -75,7 +75,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - protected readonly MutableDatabaseBackedStoreWithFileIncludes ModelStore; + protected readonly MutableDatabaseBackedStore ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; @@ -491,7 +491,7 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { // re-fetch the model on the import context. - var foundModel = ModelStore.ConsumableItems.SingleOrDefault(i => i.ID == item.ID); + var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID); if (foundModel == null || foundModel.DeletePending) return false; @@ -730,6 +730,8 @@ namespace osu.Game.Database yield return f.Filename; } + private DbSet queryModel() => ContextFactory.Get().Set(); + protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; #region Event handling / delaying From 445a4bd01c70428f33bc36b603a52e80e92d0aeb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 08:51:37 +0300 Subject: [PATCH 064/198] Re-query beatmap info on database changes --- osu.Game/Database/ArchiveModelManager.cs | 1 + .../OnlinePlay/Components/ReadyButton.cs | 34 ++++--------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 81d8668196..36cc4cce39 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 08f89d8ed8..144535ed4c 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; -using System.Linq.Expressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -41,38 +39,20 @@ namespace osu.Game.Screens.OnlinePlay.Components SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); } - private void updateSelectedItem(PlaylistItem item) - { - hasBeatmap = findBeatmap(expr => beatmaps.QueryBeatmap(expr)); - } + private void updateSelectedItem(PlaylistItem _) => updateBeatmapState(); + private void beatmapUpdated(ValueChangedEvent> _) => updateBeatmapState(); + private void beatmapRemoved(ValueChangedEvent> _) => updateBeatmapState(); - private void beatmapUpdated(ValueChangedEvent> weakSet) - { - if (weakSet.NewValue.TryGetTarget(out var set)) - { - if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) - Schedule(() => hasBeatmap = true); - } - } - - private void beatmapRemoved(ValueChangedEvent> weakSet) - { - if (weakSet.NewValue.TryGetTarget(out var set)) - { - if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) - Schedule(() => hasBeatmap = false); - } - } - - private bool findBeatmap(Func>, BeatmapInfo> expression) + private void updateBeatmapState() { int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; string checksum = SelectedItem.Value?.Beatmap.Value?.MD5Hash; if (beatmapId == null || checksum == null) - return false; + return; - return expression(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum) != null; + var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); + hasBeatmap = databasedBeatmap != null && !databasedBeatmap.BeatmapSet.DeletePending; } protected override void Update() From cb7df0fe1172935581909b938cab6fe987fcc5ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:14:39 +0900 Subject: [PATCH 065/198] Add failing test for storyboard start time ordering --- .../Formats/LegacyStoryboardDecoderTest.cs | 20 +++++++++++++++++++ .../Resources/out-of-order-starttimes.osb | 6 ++++++ 2 files changed, 26 insertions(+) create mode 100644 osu.Game.Tests/Resources/out-of-order-starttimes.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 9ebedb3c80..b36597a949 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -95,6 +95,26 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestOutOfOrderStartTimes() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("out-of-order-starttimes.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); + Assert.AreEqual(2, background.Elements.Count); + + Assert.AreEqual(1500, background.Elements[0].StartTime); + Assert.AreEqual(1000, background.Elements[1].StartTime); + + Assert.AreEqual(1000, storyboard.FirstEventTime); + } + } + [Test] public void TestDecodeVariableWithSuffix() { diff --git a/osu.Game.Tests/Resources/out-of-order-starttimes.osb b/osu.Game.Tests/Resources/out-of-order-starttimes.osb new file mode 100644 index 0000000000..09988ff64e --- /dev/null +++ b/osu.Game.Tests/Resources/out-of-order-starttimes.osb @@ -0,0 +1,6 @@ +[Events] +//Storyboard Layer 0 (Background) +Sprite,Background,TopCentre,"img.jpg",320,240 + F,0,1500,1600,0,1 +Sprite,Background,TopCentre,"img.jpg",320,240 + F,0,1000,1100,0,1 From 20d04d69332f18b7f14eedc44fed5bdf0ae9e9a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:16:01 +0900 Subject: [PATCH 066/198] Fix Storyboard's FirstEventTime not finding the true earliest event --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 2 +- osu.Game/Screens/Play/GameplayClockContainer.cs | 4 +++- osu.Game/Storyboards/Storyboard.cs | 10 +++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index b36597a949..7bee580863 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(1500, background.Elements[0].StartTime); Assert.AreEqual(1000, background.Elements[1].StartTime); - Assert.AreEqual(1000, storyboard.FirstEventTime); + Assert.AreEqual(1000, storyboard.EarliestEventTime); } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0248432917..ddbb087962 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -131,7 +131,9 @@ namespace osu.Game.Screens.Play // if a storyboard is present, it may dictate the appropriate start time by having events in negative time space. // this is commonly used to display an intro before the audio track start. - startTime = Math.Min(startTime, beatmap.Storyboard.FirstEventTime); + double? firstStoryboardEvent = beatmap.Storyboard.EarliestEventTime; + if (firstStoryboardEvent != null) + startTime = Math.Min(startTime, firstStoryboardEvent.Value); // some beatmaps specify a current lead-in time which should be used instead of the ruleset-provided value when available. // this is not available as an option in the live editor but can still be applied via .osu editing. diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index e0d18eab00..d4ba18d394 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -27,7 +28,14 @@ namespace osu.Game.Storyboards public bool HasDrawable => Layers.Any(l => l.Elements.Any(e => e.IsDrawable)); - public double FirstEventTime => Layers.Min(l => l.Elements.FirstOrDefault()?.StartTime ?? 0); + /// + /// Across all layers, find the earliest point in time that a storyboard element exists at. + /// Will return null if there are no elements. + /// + /// + /// This iterates all elements and as such should be used sparingly or stored locally. + /// + public double? EarliestEventTime => Layers.SelectMany(l => l.Elements).OrderBy(e => e.StartTime).FirstOrDefault()?.StartTime; /// /// Depth of the currently front-most storyboard layer, excluding the overlay layer. From 9e0c490141e4e85ecde7c96d7a6b3f37f0b3b4fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 15:40:22 +0900 Subject: [PATCH 067/198] Remove unused using --- osu.Game/Storyboards/Storyboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index d4ba18d394..1ba25cc11e 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; From ea38b00b29adabf0ec91b0b085f2af0b11829b23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 10:27:08 +0300 Subject: [PATCH 068/198] Schedule all calls to `updateBeatmapState()` --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 144535ed4c..6f86f2b879 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -39,9 +39,9 @@ namespace osu.Game.Screens.OnlinePlay.Components SelectedItem.BindValueChanged(item => updateSelectedItem(item.NewValue), true); } - private void updateSelectedItem(PlaylistItem _) => updateBeatmapState(); - private void beatmapUpdated(ValueChangedEvent> _) => updateBeatmapState(); - private void beatmapRemoved(ValueChangedEvent> _) => updateBeatmapState(); + private void updateSelectedItem(PlaylistItem _) => Scheduler.AddOnce(updateBeatmapState); + private void beatmapUpdated(ValueChangedEvent> _) => Scheduler.AddOnce(updateBeatmapState); + private void beatmapRemoved(ValueChangedEvent> _) => Scheduler.AddOnce(updateBeatmapState); private void updateBeatmapState() { From 485a57776b205581528925011a1d2ec77d8fc26d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Jan 2021 10:28:41 +0300 Subject: [PATCH 069/198] Fix `hasBeatmap` potentially checking on outdated `DeletePending` value --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 6f86f2b879..b782df75df 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -52,7 +52,15 @@ namespace osu.Game.Screens.OnlinePlay.Components return; var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - hasBeatmap = databasedBeatmap != null && !databasedBeatmap.BeatmapSet.DeletePending; + + if (databasedBeatmap == null) + hasBeatmap = false; + else + { + // DeletePending isn't updated in the beatmap info query above, need to directly query the beatmap set from database as well. + var databasedSet = beatmaps.QueryBeatmapSet(s => s.Equals(databasedBeatmap.BeatmapSet)); + hasBeatmap = databasedSet?.DeletePending == false; + } } protected override void Update() From ba4e41142262d683e25185af98e13a07157214ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:37:07 +0900 Subject: [PATCH 070/198] Clone and copy ControlPointInfo when retrieving a playable beatmap --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 17 +++++++++++++++++ .../Beatmaps/ControlPoints/ControlPointInfo.cs | 12 +++++++++++- .../ControlPoints/DifficultyControlPoint.cs | 7 +++++++ .../ControlPoints/EffectControlPoint.cs | 8 ++++++++ .../ControlPoints/SampleControlPoint.cs | 8 ++++++++ .../ControlPoints/TimingControlPoint.cs | 8 ++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 15 +++++++++++++-- osu.Game/Beatmaps/IBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 3 +++ osu.Game/Screens/Edit/EditorBeatmap.cs | 6 +++++- osu.Game/Screens/Play/GameplayBeatmap.cs | 6 +++++- 11 files changed, 86 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index c6649f6af1..090675473d 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -28,5 +28,22 @@ namespace osu.Game.Beatmaps.ControlPoints /// An existing control point to compare with. /// Whether this is redundant when placed alongside . public abstract bool IsRedundant(ControlPoint existing); + + /// + /// Create a copy of this room without online information. + /// Should be used to create a local copy of a room for submitting in the future. + /// + public ControlPoint CreateCopy() + { + var copy = (ControlPoint)Activator.CreateInstance(GetType()); + + copy.CopyFrom(this); + + return copy; + } + + public virtual void CopyFrom(ControlPoint other) + { + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index b843aad950..b56f9a106b 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -11,7 +11,7 @@ using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo + public class ControlPointInfo : ICloneable { /// /// All control points grouped by time. @@ -297,5 +297,15 @@ namespace osu.Game.Beatmaps.ControlPoints break; } } + + public object Clone() + { + var controlPointInfo = new ControlPointInfo(); + + foreach (var point in AllControlPoints) + controlPointInfo.Add(point.Time, point.CreateCopy()); + + return controlPointInfo; + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 283bf76572..0bc5605051 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -39,5 +39,12 @@ namespace osu.Game.Beatmaps.ControlPoints public override bool IsRedundant(ControlPoint existing) => existing is DifficultyControlPoint existingDifficulty && SpeedMultiplier == existingDifficulty.SpeedMultiplier; + + public override void CopyFrom(ControlPoint other) + { + SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs index ea28fca170..79bc88e773 100644 --- a/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs @@ -50,5 +50,13 @@ namespace osu.Game.Beatmaps.ControlPoints && existing is EffectControlPoint existingEffect && KiaiMode == existingEffect.KiaiMode && OmitFirstBarLine == existingEffect.OmitFirstBarLine; + + public override void CopyFrom(ControlPoint other) + { + KiaiMode = ((EffectControlPoint)other).KiaiMode; + OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index fd0b496335..4aa6a3d6e9 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -72,5 +72,13 @@ namespace osu.Game.Beatmaps.ControlPoints => existing is SampleControlPoint existingSample && SampleBank == existingSample.SampleBank && SampleVolume == existingSample.SampleVolume; + + public override void CopyFrom(ControlPoint other) + { + SampleVolume = ((SampleControlPoint)other).SampleVolume; + SampleBank = ((SampleControlPoint)other).SampleBank; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index d9378bca4a..580642f593 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -69,5 +69,13 @@ namespace osu.Game.Beatmaps.ControlPoints // Timing points are never redundant as they can change the time signature. public override bool IsRedundant(ControlPoint existing) => false; + + public override void CopyFrom(ControlPoint other) + { + TimeSignature = ((TimingControlPoint)other).TimeSignature; + BeatLength = ((TimingControlPoint)other).BeatLength; + + base.CopyFrom(other); + } } } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index c9d139bdd0..06ff677aed 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,13 +164,24 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public readonly float BpmMultiplier; + public float BpmMultiplier { get; set; } public LegacyDifficultyControlPoint(double beatLength) + : this() + { + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + } + + public LegacyDifficultyControlPoint() { SpeedMultiplierBindable.Precision = double.Epsilon; + } - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + public override void CopyFrom(ControlPoint other) + { + base.CopyFrom(other); + + BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier; } } diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 8f27e0b0e9..7dd85e1232 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps /// /// The control points in this beatmap. /// - ControlPointInfo ControlPointInfo { get; } + ControlPointInfo ControlPointInfo { get; set; } /// /// The breaks in this beatmap. diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 30382c444f..06b5913b18 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Statistics; using osu.Framework.Testing; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; @@ -111,6 +112,8 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); + converted.ControlPointInfo = (ControlPointInfo)converted.ControlPointInfo.Clone(); + // Apply conversion mods to the result foreach (var mod in mods.OfType()) { diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 165d2ba278..a54a95f59d 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -74,7 +74,11 @@ namespace osu.Game.Screens.Edit public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; + public ControlPointInfo ControlPointInfo + { + get => PlayableBeatmap.ControlPointInfo; + set => PlayableBeatmap.ControlPointInfo = value; + } public List Breaks => PlayableBeatmap.Breaks; diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index 64894544f4..565595656f 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -29,7 +29,11 @@ namespace osu.Game.Screens.Play public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; + public ControlPointInfo ControlPointInfo + { + get => PlayableBeatmap.ControlPointInfo; + set => PlayableBeatmap.ControlPointInfo = value; + } public List Breaks => PlayableBeatmap.Breaks; From b4a779108e7c87994198690cba57ca05e1a0e503 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:37:49 +0900 Subject: [PATCH 071/198] Ensure working beatmap is reloaded on exiting the editor --- osu.Game/Screens/Edit/Editor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 223c678fba..a9fe7ba38d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -483,6 +483,8 @@ namespace osu.Game.Screens.Edit Background.FadeColour(Color4.White, 500); resetTrack(); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo); + return base.OnExiting(next); } From 7fdf876b4c8fb19020033d080fca3ee08dd44adc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:38:15 +0900 Subject: [PATCH 072/198] Fix editor timing screen mutating the WorkingBeatmap instead of EditorBeatmap --- osu.Game/Screens/Edit/Timing/DifficultySection.cs | 2 +- osu.Game/Screens/Edit/Timing/EffectSection.cs | 2 +- osu.Game/Screens/Edit/Timing/GroupSection.cs | 9 ++++----- osu.Game/Screens/Edit/Timing/SampleSection.cs | 2 +- osu.Game/Screens/Edit/Timing/Section.cs | 3 +-- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 11 +++++------ osu.Game/Screens/Edit/Timing/TimingSection.cs | 2 +- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/DifficultySection.cs b/osu.Game/Screens/Edit/Timing/DifficultySection.cs index b55d74e3b4..b87b8961f8 100644 --- a/osu.Game/Screens/Edit/Timing/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Timing/DifficultySection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Timing protected override DifficultyControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time); return new DifficultyControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/EffectSection.cs b/osu.Game/Screens/Edit/Timing/EffectSection.cs index 2f143108a9..6d23b52c05 100644 --- a/osu.Game/Screens/Edit/Timing/EffectSection.cs +++ b/osu.Game/Screens/Edit/Timing/EffectSection.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Timing protected override EffectControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.EffectPointAt(SelectedGroup.Value.Time); return new EffectControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs index 2605ea8b75..2e2c380d4a 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -24,7 +23,7 @@ namespace osu.Game.Screens.Edit.Timing protected Bindable SelectedGroup { get; private set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] private EditorClock clock { get; set; } @@ -107,13 +106,13 @@ namespace osu.Game.Screens.Edit.Timing var currentGroupItems = SelectedGroup.Value.ControlPoints.ToArray(); - Beatmap.Value.Beatmap.ControlPointInfo.RemoveGroup(SelectedGroup.Value); + Beatmap.ControlPointInfo.RemoveGroup(SelectedGroup.Value); foreach (var cp in currentGroupItems) - Beatmap.Value.Beatmap.ControlPointInfo.Add(time, cp); + Beatmap.ControlPointInfo.Add(time, cp); // the control point might not necessarily exist yet, if currentGroupItems was empty. - SelectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.GroupAt(time, true); + SelectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(time, true); changeHandler?.EndChange(); } diff --git a/osu.Game/Screens/Edit/Timing/SampleSection.cs b/osu.Game/Screens/Edit/Timing/SampleSection.cs index 280e19c99a..cc73af6349 100644 --- a/osu.Game/Screens/Edit/Timing/SampleSection.cs +++ b/osu.Game/Screens/Edit/Timing/SampleSection.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Timing protected override SampleControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time); return new SampleControlPoint { diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs index 7a81eeb1a4..5269fa9774 100644 --- a/osu.Game/Screens/Edit/Timing/Section.cs +++ b/osu.Game/Screens/Edit/Timing/Section.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -27,7 +26,7 @@ namespace osu.Game.Screens.Edit.Timing private const float header_height = 20; [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] protected Bindable SelectedGroup { get; private set; } diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index eab909b798..c5d2dd756a 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -62,7 +61,7 @@ namespace osu.Game.Screens.Edit.Timing private EditorClock clock { get; set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } [Resolved] private Bindable selectedGroup { get; set; } @@ -124,7 +123,7 @@ namespace osu.Game.Screens.Edit.Timing selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true); - controlPointGroups.BindTo(Beatmap.Value.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { table.ControlGroups = controlPointGroups; @@ -137,14 +136,14 @@ namespace osu.Game.Screens.Edit.Timing if (selectedGroup.Value == null) return; - Beatmap.Value.Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value); + Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value); - selectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime); + selectedGroup.Value = Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime); } private void addNew() { - selectedGroup.Value = Beatmap.Value.Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true); + selectedGroup.Value = Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true); } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index 1ae2a86885..a0bb9ac506 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Edit.Timing protected override TimingControlPoint CreatePoint() { - var reference = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(SelectedGroup.Value.Time); + var reference = Beatmap.ControlPointInfo.TimingPointAt(SelectedGroup.Value.Time); return new TimingControlPoint { From b7dd54847fc97e98ac5d0eef7d06b5b4d12b509b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 16:47:08 +0900 Subject: [PATCH 073/198] Move resolved usage of WorkingBeatmap in editor components as local as possible to avoid misuse --- .../Screens/Edit/Compose/ComposeScreen.cs | 5 ++++- osu.Game/Screens/Edit/EditorScreen.cs | 5 ----- .../Screens/Edit/Setup/DifficultySection.cs | 16 ++++++++-------- .../Screens/Edit/Setup/MetadataSection.cs | 16 ++++++++-------- .../Screens/Edit/Setup/ResourcesSection.cs | 19 +++++++++++-------- osu.Game/Screens/Edit/Setup/SetupSection.cs | 4 +--- 6 files changed, 32 insertions(+), 33 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index c297a03dbf..81b1195a40 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -16,6 +16,9 @@ namespace osu.Game.Screens.Edit.Compose { public class ComposeScreen : EditorScreenWithTimeline { + [Resolved] + private IBindable beatmap { get; set; } + private HitObjectComposer composer; public ComposeScreen() @@ -59,7 +62,7 @@ namespace osu.Game.Screens.Edit.Compose { Debug.Assert(ruleset != null); - var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); + var beatmapSkinProvider = new BeatmapSkinProvidingContainer(beatmap.Value.Skin); // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation // full access to all skin sources. diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 4d62a7d3cd..7fbb6a8ca0 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -2,10 +2,8 @@ // 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; -using osu.Game.Beatmaps; namespace osu.Game.Screens.Edit { @@ -14,9 +12,6 @@ namespace osu.Game.Screens.Edit /// public abstract class EditorScreen : Container { - [Resolved] - protected IBindable Beatmap { get; private set; } - [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 897ddc6955..f180d7e63e 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Object Size", Description = "The size of all hit objects", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Health Drain", Description = "The rate of passive health drain throughout playable time", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.DrainRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Approach Rate", Description = "The speed at which objects are presented to the player", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Setup { Label = "Overall Difficulty", Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", - Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty) + Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) { Default = BeatmapDifficulty.DEFAULT_DIFFICULTY, MinValue = 0, @@ -88,10 +88,10 @@ namespace osu.Game.Screens.Edit.Setup { // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; - Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; + Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; editorBeatmap.UpdateAllHitObjects(); } diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 4ddee2acc6..e812c042fb 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -29,25 +29,25 @@ namespace osu.Game.Screens.Edit.Setup artistTextBox = new LabelledTextBox { Label = "Artist", - Current = { Value = Beatmap.Value.Metadata.Artist }, + Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { Label = "Title", - Current = { Value = Beatmap.Value.Metadata.Title }, + Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { Label = "Creator", - Current = { Value = Beatmap.Value.Metadata.AuthorString }, + Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { Label = "Difficulty Name", - Current = { Value = Beatmap.Value.BeatmapInfo.Version }, + Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, }; @@ -62,10 +62,10 @@ namespace osu.Game.Screens.Edit.Setup // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Value.Metadata.Artist = artistTextBox.Current.Value; - Beatmap.Value.Metadata.Title = titleTextBox.Current.Value; - Beatmap.Value.Metadata.AuthorString = creatorTextBox.Current.Value; - Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value; + Beatmap.Metadata.Artist = artistTextBox.Current.Value; + Beatmap.Metadata.Title = titleTextBox.Current.Value; + Beatmap.Metadata.AuthorString = creatorTextBox.Current.Value; + Beatmap.BeatmapInfo.Version = difficultyTextBox.Current.Value; } } } diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 0c957b80af..b3bceceea3 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -42,6 +42,9 @@ namespace osu.Game.Screens.Edit.Setup [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private IBindable working { get; set; } + [Resolved(canBeNull: true)] private Editor editor { get; set; } @@ -70,7 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox { Label = "Audio Track", - Current = { Value = Beatmap.Value.Metadata.AudioFile ?? "Click to select a track" }, + Current = { Value = Beatmap.Metadata.AudioFile ?? "Click to select a track" }, Target = audioTrackFileChooserContainer, TabbableContentContainer = this }, @@ -115,11 +118,11 @@ namespace osu.Game.Screens.Edit.Setup if (!info.Exists) return false; - var set = Beatmap.Value.BeatmapSetInfo; + var set = working.Value.BeatmapSetInfo; // remove the previous background for now. // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.BackgroundFile); + var oldFile = set.Files.FirstOrDefault(f => f.Filename == working.Value.Metadata.BackgroundFile); using (var stream = info.OpenRead()) { @@ -129,7 +132,7 @@ namespace osu.Game.Screens.Edit.Setup beatmaps.AddFile(set, stream, info.Name); } - Beatmap.Value.Metadata.BackgroundFile = info.Name; + working.Value.Metadata.BackgroundFile = info.Name; updateBackgroundSprite(); return true; @@ -148,11 +151,11 @@ namespace osu.Game.Screens.Edit.Setup if (!info.Exists) return false; - var set = Beatmap.Value.BeatmapSetInfo; + var set = working.Value.BeatmapSetInfo; // remove the previous audio track for now. // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.AudioFile); + var oldFile = set.Files.FirstOrDefault(f => f.Filename == working.Value.Metadata.AudioFile); using (var stream = info.OpenRead()) { @@ -162,7 +165,7 @@ namespace osu.Game.Screens.Edit.Setup beatmaps.AddFile(set, stream, info.Name); } - Beatmap.Value.Metadata.AudioFile = info.Name; + working.Value.Metadata.AudioFile = info.Name; music.ReloadCurrentTrack(); @@ -178,7 +181,7 @@ namespace osu.Game.Screens.Edit.Setup private void updateBackgroundSprite() { - LoadComponentAsync(new BeatmapBackgroundSprite(Beatmap.Value) + LoadComponentAsync(new BeatmapBackgroundSprite(working.Value) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index cdf17d355e..88521a8fb0 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -2,10 +2,8 @@ // 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; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osuTK; @@ -19,7 +17,7 @@ namespace osu.Game.Screens.Edit.Setup protected OsuColour Colours { get; private set; } [Resolved] - protected IBindable Beatmap { get; private set; } + protected EditorBeatmap Beatmap { get; private set; } protected override Container Content => flow; From 3b08faa0ea134d99f6cd9dcd02096df1ffac115a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 17:49:11 +0900 Subject: [PATCH 074/198] Fix RemoveBlockingOverlay causing transform mutation from disposal threads --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 17831ed26b..36e3078653 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -151,11 +151,11 @@ namespace osu.Game updateBlockingOverlayFade(); } - public void RemoveBlockingOverlay(OverlayContainer overlay) + public void RemoveBlockingOverlay(OverlayContainer overlay) => Schedule(() => { visibleBlockingOverlays.Remove(overlay); updateBlockingOverlayFade(); - } + }); /// /// Close all game-wide overlays. From 2d1b52be0d8e02ffc4c4fbbd6a9f8f621d54f211 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 17:21:31 +0000 Subject: [PATCH 075/198] Moved "ToolbarSocialButton" This will remove it from coming off the screen. --- osu.Game/Overlays/Toolbar/Toolbar.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 393e349bd0..88a2518f84 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,12 +69,13 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { + + new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), new ToolbarChatButton(), - new ToolbarSocialButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 2e2b3ab5d4f66cd0295cd939a209c51485d59b41 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 17:26:42 +0000 Subject: [PATCH 076/198] Should remove codeFactor error --- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 88a2518f84..c95d02cea4 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,7 +69,6 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { - new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), From 73f5e5aaf9fb94726b6906f98b6493202fedb680 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:03:51 +0000 Subject: [PATCH 077/198] Moved "ToolbarSocialButton" back --- osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index c95d02cea4..393e349bd0 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,12 +69,12 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { - new ToolbarSocialButton(), new ToolbarNewsButton(), new ToolbarChangelogButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), new ToolbarChatButton(), + new ToolbarSocialButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From 3468df840b37291dfdae94af3fb0d29a8e7493fa Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:04:30 +0000 Subject: [PATCH 078/198] Moved tooltip to the left to stop the overflow --- osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index e62c7bc807..ca334702ce 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -2,12 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarSocialButton : ToolbarOverlayToggleButton { + + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarSocialButton() { Hotkey = GlobalAction.ToggleSocial; From 0e42d415c18888192358e4f7e2a41074ebd0530f Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 21:05:28 +0000 Subject: [PATCH 079/198] Hit another oopie --- osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index ca334702ce..1e00afc5fd 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -9,7 +9,6 @@ namespace osu.Game.Overlays.Toolbar { public class ToolbarSocialButton : ToolbarOverlayToggleButton { - protected override Anchor TooltipAnchor => Anchor.TopRight; public ToolbarSocialButton() From 1234d0fa0409c786c4e4ec4e2a63b5a5da406292 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 22:01:12 +0000 Subject: [PATCH 080/198] Applied all tooltips to the right --- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs | 5 ++++- osu.Game/Overlays/Toolbar/ToolbarChatButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs | 3 +++ osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs | 3 +++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 0363873326..c495d673ce 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarBeatmapListingButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarBeatmapListingButton() { Hotkey = GlobalAction.ToggleDirect; diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs index 23f8b141b2..28112d178f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarChangelogButton : ToolbarOverlayToggleButton - { + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(ChangelogOverlay changelog) { diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index f9a66ae7bb..2d3b33e9bc 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -2,12 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarChatButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarChatButton() { Hotkey = GlobalAction.ToggleChat; diff --git a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs index 0ba2935c80..9b2573ad07 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarNewsButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(NewsOverlay news) { diff --git a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs index 22a01bcdb5..312fc41aab 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarRankingsButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + [BackgroundDependencyLoader(true)] private void load(RankingsOverlay rankings) { From 77e660e42677eee4d2407b3b59a649f836b35f34 Mon Sep 17 00:00:00 2001 From: KyeKiller Date: Mon, 4 Jan 2021 22:11:52 +0000 Subject: [PATCH 081/198] Should pass all checks again now. --- osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs index 28112d178f..86bc73361a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; namespace osu.Game.Overlays.Toolbar { public class ToolbarChangelogButton : ToolbarOverlayToggleButton - { + { protected override Anchor TooltipAnchor => Anchor.TopRight; [BackgroundDependencyLoader(true)] From 81355652fa8601d4537cb6998afe6208da1b925d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Jan 2021 06:00:15 +0300 Subject: [PATCH 082/198] Add simple test coverage --- .../TestSceneMultiplayerReadyButton.cs | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 6b11613f1c..03ba73d35b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -7,8 +7,10 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; @@ -23,6 +25,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneMultiplayerReadyButton : MultiplayerTestScene { private MultiplayerReadyButton button; + private BeatmapSetInfo importedSet; private BeatmapManager beatmaps; private RulesetStore rulesets; @@ -38,9 +41,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - var beatmap = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First().Beatmaps.First(); - - Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); + Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); Child = button = new MultiplayerReadyButton { @@ -51,13 +53,30 @@ namespace osu.Game.Tests.Visual.Multiplayer { Value = new PlaylistItem { - Beatmap = { Value = beatmap }, - Ruleset = { Value = beatmap.Ruleset } + Beatmap = { Value = Beatmap.Value.BeatmapInfo }, + Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset } } } }; }); + [Test] + public void TestDeletedBeatmapDisableReady() + { + OsuButton readyButton = null; + + AddAssert("ensure ready button enabled", () => + { + readyButton = button.ChildrenOfType().Single(); + return readyButton.Enabled.Value; + }); + + AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); + AddAssert("ready button disabled", () => !readyButton.Enabled.Value); + AddStep("undelete beatmap", () => beatmaps.Undelete(importedSet)); + AddAssert("ready button enabled back", () => readyButton.Enabled.Value); + } + [Test] public void TestToggleStateWhenNotHost() { From caa88c6100cb17d3307336ab43e00f16fbe98b01 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:13:51 +0900 Subject: [PATCH 083/198] Use CreateCopy instead of Clone interface I was going for conformity by using the IClonable interface, but it doesn't look like we use it anywhere else in the project. --- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index b56f9a106b..e8a91e4001 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -11,7 +11,7 @@ using osu.Framework.Lists; namespace osu.Game.Beatmaps.ControlPoints { [Serializable] - public class ControlPointInfo : ICloneable + public class ControlPointInfo { /// /// All control points grouped by time. @@ -298,7 +298,7 @@ namespace osu.Game.Beatmaps.ControlPoints } } - public object Clone() + public ControlPointInfo CreateCopy() { var controlPointInfo = new ControlPointInfo(); diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 06b5913b18..8688017887 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); - converted.ControlPointInfo = (ControlPointInfo)converted.ControlPointInfo.Clone(); + converted.ControlPointInfo = converted.ControlPointInfo.CreateCopy(); // Apply conversion mods to the result foreach (var mod in mods.OfType()) From 385c9cd2e23e9f66b3b316b11b594c5377d27b77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:14:16 +0900 Subject: [PATCH 084/198] Add test coverage --- .../NonVisual/ControlPointInfoTest.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs index 90a487c0ac..b27c257795 100644 --- a/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs +++ b/osu.Game.Tests/NonVisual/ControlPointInfoTest.cs @@ -246,5 +246,32 @@ namespace osu.Game.Tests.NonVisual Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0)); } + + [Test] + public void TestCreateCopyIsDeepClone() + { + var cpi = new ControlPointInfo(); + + cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); + + var cpiCopy = cpi.CreateCopy(); + + cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 }); + + Assert.That(cpi.Groups.Count, Is.EqualTo(1)); + Assert.That(cpiCopy.Groups.Count, Is.EqualTo(2)); + + Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1)); + Assert.That(cpiCopy.TimingPoints.Count, Is.EqualTo(2)); + + Assert.That(cpi.TimingPoints[0], Is.Not.SameAs(cpiCopy.TimingPoints[0])); + Assert.That(cpi.TimingPoints[0].BeatLengthBindable, Is.Not.SameAs(cpiCopy.TimingPoints[0].BeatLengthBindable)); + + Assert.That(cpi.TimingPoints[0].BeatLength, Is.EqualTo(cpiCopy.TimingPoints[0].BeatLength)); + + cpi.TimingPoints[0].BeatLength = 800; + + Assert.That(cpi.TimingPoints[0].BeatLength, Is.Not.EqualTo(cpiCopy.TimingPoints[0].BeatLength)); + } } } From 6b8e1913eee030038ed4af72be5168d0c2ba7fab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:27:45 +0900 Subject: [PATCH 085/198] Fix dependency not always available due to nested LoadComponentAsync call --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b9457f422a..2d623a200c 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -30,16 +30,16 @@ namespace osu.Game.Screens.Edit { } + private Container mainContent; + + private LoadingSpinner spinner; + [BackgroundDependencyLoader(true)] private void load([CanBeNull] BindableBeatDivisor beatDivisor) { if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); - Container mainContent; - - LoadingSpinner spinner; - Children = new Drawable[] { mainContent = new Container @@ -99,6 +99,11 @@ namespace osu.Game.Screens.Edit } }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); LoadComponentAsync(CreateMainContent(), content => { From afab35a31ad71e8f25e0d9084dc4edbb8e0183b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 13:41:31 +0900 Subject: [PATCH 086/198] Fix missing copy implementation in LegacySampleControlPiont --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 06ff677aed..a06ad35b89 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -203,6 +203,13 @@ namespace osu.Game.Beatmaps.Formats => base.IsRedundant(existing) && existing is LegacySampleControlPoint existingSample && CustomSampleBank == existingSample.CustomSampleBank; + + public override void CopyFrom(ControlPoint other) + { + base.CopyFrom(other); + + CustomSampleBank = ((LegacySampleControlPoint)other).CustomSampleBank; + } } } } From 31a6e9b860ceb9ee1ece9441beb41b8aca042f5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 14:24:49 +0900 Subject: [PATCH 087/198] Remove unused using --- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8688017887..d25adca92b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Statistics; using osu.Framework.Testing; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; From ed6ffe2ef1b55d957851391d4635d0e93585dcf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 14:54:59 +0900 Subject: [PATCH 088/198] Remove hacky code --- osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index b782df75df..64ddba669d 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -53,14 +53,7 @@ namespace osu.Game.Screens.OnlinePlay.Components var databasedBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - if (databasedBeatmap == null) - hasBeatmap = false; - else - { - // DeletePending isn't updated in the beatmap info query above, need to directly query the beatmap set from database as well. - var databasedSet = beatmaps.QueryBeatmapSet(s => s.Equals(databasedBeatmap.BeatmapSet)); - hasBeatmap = databasedSet?.DeletePending == false; - } + hasBeatmap = databasedBeatmap?.BeatmapSet?.DeletePending == false; } protected override void Update() From 962c95dc0158bfddf43ad8128dc4e1db2a131cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 19:06:00 +0900 Subject: [PATCH 089/198] Fix ModSelection making unsafe advances of ModSection --- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0107f94dcf..d00a365dde 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -130,7 +130,7 @@ namespace osu.Game.Overlays.Mods /// Select one or more mods in this section and deselects all other ones. /// /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) + public void SelectTypes(IEnumerable modTypes) => Schedule(() => { foreach (var button in buttons) { @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods else button.Deselect(); } - } + }); protected ModSection() { From a3e4e2f6c394544efae9df90228690fbdd3660c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 18:24:21 +0900 Subject: [PATCH 090/198] Switch ResultsScreen and SongSelect inheritance and remove local implementation --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 +--- osu.Game/Screens/Select/SongSelect.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 528a1842af..98794627fe 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -25,7 +25,7 @@ using osuTK; namespace osu.Game.Screens.Ranking { - public abstract class ResultsScreen : OsuScreen, IKeyBindingHandler + public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { protected const float BACKGROUND_BLUR = 20; private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Ranking // Temporary for now to stop dual transitions. Should respect the current toolbar mode, but there's no way to do so currently. public override bool HideOverlaysOnEnter => true; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public readonly Bindable SelectedScore = new Bindable(); public readonly ScoreInfo Score; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a5252fdc96..50a1dd6edf 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -41,7 +41,7 @@ using System.Diagnostics; namespace osu.Game.Screens.Select { - public abstract class SongSelect : OsuScreen, IKeyBindingHandler + public abstract class SongSelect : ScreenWithBeatmapBackground, IKeyBindingHandler { public static readonly float WEDGE_HEIGHT = 245; @@ -76,8 +76,6 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable> selectedMods { get; set; } - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - protected BeatmapCarousel Carousel { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; From b3f08b29ca60b99f6c3aa2f3a5d3dec679452a06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 18:32:23 +0900 Subject: [PATCH 091/198] Ensure that all changes to screen backgrounds are on the correct thread --- .../Background/TestSceneUserDimBackgrounds.cs | 55 ++++++++++++------- osu.Game/Rulesets/Mods/ModCinema.cs | 2 +- osu.Game/Screens/BackgroundScreen.cs | 2 + osu.Game/Screens/Edit/Editor.cs | 13 +++-- osu.Game/Screens/Menu/MainMenu.cs | 6 +- osu.Game/Screens/OsuScreen.cs | 21 +++++-- osu.Game/Screens/Play/EpilepsyWarning.cs | 21 ++++++- osu.Game/Screens/Play/Player.cs | 17 ++++-- osu.Game/Screens/Play/PlayerLoader.cs | 32 +++++++---- .../Play/ScreenWithBeatmapBackground.cs | 3 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 14 +++-- osu.Game/Screens/Select/SongSelect.cs | 6 +- 12 files changed, 132 insertions(+), 60 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 5323f58a66..b5df3285f0 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Background }); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur)); } /// @@ -106,6 +106,7 @@ namespace osu.Game.Tests.Visual.Background public void TestStoryboardBackgroundVisibility() { performFullSetup(); + AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent()); createFakeStoryboard(); AddStep("Enable Storyboard", () => { @@ -198,8 +199,9 @@ namespace osu.Game.Tests.Visual.Background }))); AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); + AddUntilStep("Screen is undimmed, original background retained", () => - songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); + songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur)); } /// @@ -224,7 +226,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("Resume PlayerLoader", () => player.Restart()); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect()); + AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur)); } private void createFakeStoryboard() => AddStep("Create storyboard", () => @@ -274,9 +276,11 @@ namespace osu.Game.Tests.Visual.Background private class DummySongSelect : PlaySongSelect { + private FadeAccessibleBackground background; + protected override BackgroundScreen CreateBackground() { - FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value); + background = new FadeAccessibleBackground(Beatmap.Value); DimEnabled.BindTo(background.EnableUserDim); return background; } @@ -294,42 +298,54 @@ namespace osu.Game.Tests.Visual.Background config.BindWith(OsuSetting.BlurLevel, BlurLevel); } - public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim); + public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim); - public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; + public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White; - public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); + public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR); - public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0); + public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0); - public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0; + public bool IsBackgroundInvisible() => background.CurrentAlpha == 0; - public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1; + public bool IsBackgroundVisible() => background.CurrentAlpha == 1; - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); + + public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected; /// /// Make sure every time a screen gets pushed, the background doesn't get replaced /// /// Whether or not the original background (The one created in DummySongSelect) is still the current background - public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen(); + public bool IsBackgroundCurrent() => background?.IsCurrentScreen() == true; } private class FadeAccessibleResults : ResultsScreen { + private FadeAccessibleBackground background; + public FadeAccessibleResults(ScoreInfo score) : base(score, true) { } - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); } private class LoadBlockingTestPlayer : TestPlayer { - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => + new FadeAccessibleBackground(Beatmap.Value); + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground)); + } public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard; @@ -354,15 +370,16 @@ namespace osu.Game.Tests.Visual.Background Thread.Sleep(1); StoryboardEnabled = config.GetBindable(OsuSetting.ShowStoryboard); - ReplacesBackground.BindTo(Background.StoryboardReplacesBackground); DrawableRuleset.IsPaused.BindTo(IsPaused); } } private class TestPlayerLoader : PlayerLoader { + private FadeAccessibleBackground background; + public VisualSettings VisualSettingsPos => VisualSettings; - public BackgroundScreen ScreenPos => Background; + public BackgroundScreen ScreenPos => background; public TestPlayerLoader(Player player) : base(() => player) @@ -371,9 +388,9 @@ namespace osu.Game.Tests.Visual.Background public void TriggerOnHover() => OnHover(new HoverEvent(new InputState())); - public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); + public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); - protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); } private class FadeAccessibleBackground : BackgroundScreenBeatmap diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index cf8128301c..bee9e56edd 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.Background.EnableUserDim.Value = false; + player.ApplyToBackground(b => b.EnableUserDim.Value = false); player.DimmableStoryboard.IgnoreUserSettings.Value = true; diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index 0f3615b7a9..ea220c2f82 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -34,6 +34,8 @@ namespace osu.Game.Screens return false; } + public void ApplyToBackground(Action action) => Schedule(() => action.Invoke(this)); + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 223c678fba..8c34cb2e08 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -444,11 +444,14 @@ namespace osu.Game.Screens.Edit { base.OnEntering(last); - // todo: temporary. we want to be applying dim using the UserDimContainer eventually. - Background.FadeColour(Color4.DarkGray, 500); + ApplyToBackground(b => + { + // todo: temporary. we want to be applying dim using the UserDimContainer eventually. + b.FadeColour(Color4.DarkGray, 500); - Background.EnableUserDim.Value = false; - Background.BlurAmount.Value = 0; + b.EnableUserDim.Value = false; + b.BlurAmount.Value = 0; + }); resetTrack(true); } @@ -480,7 +483,7 @@ namespace osu.Game.Screens.Edit } } - Background.FadeColour(Color4.White, 500); + ApplyToBackground(b => b.FadeColour(Color4.White, 500)); resetTrack(); return base.OnExiting(next); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 9d5720ff34..97fd58318b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -127,11 +127,11 @@ namespace osu.Game.Screens.Menu { case ButtonSystemState.Initial: case ButtonSystemState.Exit: - Background.FadeColour(Color4.White, 500, Easing.OutSine); + ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine)); break; default: - Background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine)); break; } }; @@ -256,7 +256,7 @@ namespace osu.Game.Screens.Menu { base.OnResuming(last); - (Background as BackgroundScreenDefault)?.Next(); + ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next()); // we may have consumed our preloaded instance, so let's make another. preloadSongSelect(); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 851aedd84f..c97c0aef2b 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -114,9 +114,14 @@ namespace osu.Game.Screens Mods = screenDependencies.Mods; } - protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen; + public void ApplyToBackground(Action action) => background.ApplyToBackground(action); - private BackgroundScreen localBackground; + /// + /// The background created and owned by this screen. May be null if the background didn't change. + /// + private BackgroundScreen ownedBackground; + + private BackgroundScreen background; [Resolved(canBeNull: true)] private BackgroundScreenStack backgroundStack { get; set; } @@ -160,7 +165,15 @@ namespace osu.Game.Screens { applyArrivingDefaults(false); - backgroundStack?.Push(localBackground = CreateBackground()); + backgroundStack?.Push(ownedBackground = CreateBackground()); + + background = backgroundStack?.CurrentScreen as BackgroundScreen; + + if (background != ownedBackground) + { + // background may have not been replaced, at which point we don't want to track the background lifetime. + ownedBackground = null; + } base.OnEntering(last); } @@ -173,7 +186,7 @@ namespace osu.Game.Screens if (base.OnExiting(next)) return true; - if (localBackground != null && backgroundStack?.CurrentScreen == localBackground) + if (ownedBackground != null && backgroundStack?.CurrentScreen == ownedBackground) backgroundStack?.Exit(); return false; diff --git a/osu.Game/Screens/Play/EpilepsyWarning.cs b/osu.Game/Screens/Play/EpilepsyWarning.cs index dc42427fbf..89e25d849f 100644 --- a/osu.Game/Screens/Play/EpilepsyWarning.cs +++ b/osu.Game/Screens/Play/EpilepsyWarning.cs @@ -24,7 +24,19 @@ namespace osu.Game.Screens.Play Alpha = 0f; } - public BackgroundScreenBeatmap DimmableBackground { get; set; } + private BackgroundScreenBeatmap dimmableBackground; + + public BackgroundScreenBeatmap DimmableBackground + { + get => dimmableBackground; + set + { + dimmableBackground = value; + + if (IsLoaded) + updateBackgroundFade(); + } + } [BackgroundDependencyLoader] private void load(OsuColour colours, IBindable beatmap) @@ -75,11 +87,16 @@ namespace osu.Game.Screens.Play protected override void PopIn() { - DimmableBackground?.FadeColour(OsuColour.Gray(0.5f), FADE_DURATION, Easing.OutQuint); + updateBackgroundFade(); this.FadeIn(FADE_DURATION, Easing.OutQuint); } + private void updateBackgroundFade() + { + DimmableBackground?.FadeColour(OsuColour.Gray(0.5f), FADE_DURATION, Easing.OutQuint); + } + protected override void PopOut() => this.FadeOut(FADE_DURATION); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bf2e6f5379..1fcbed7ef7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -721,15 +721,20 @@ namespace osu.Game.Screens.Play .Delay(250) .FadeIn(250); - Background.EnableUserDim.Value = true; - Background.BlurAmount.Value = 0; + ApplyToBackground(b => + { + b.EnableUserDim.Value = true; + b.BlurAmount.Value = 0; + + // bind component bindables. + b.IsBreakTime.BindTo(breakTracker.IsBreakTime); + + b.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); + }); - // bind component bindables. - Background.IsBreakTime.BindTo(breakTracker.IsBreakTime); HUDOverlay.IsBreakTime.BindTo(breakTracker.IsBreakTime); DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime); - Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable; @@ -875,7 +880,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - Background.EnableUserDim.Value = false; + ApplyToBackground(b => b.EnableUserDim.Value = false); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f59b36bc42..5b4bd11216 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play backgroundBrightnessReduction = value; - Background.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200)); } } @@ -176,12 +176,17 @@ namespace osu.Game.Screens.Play { base.OnEntering(last); - if (epilepsyWarning != null) - epilepsyWarning.DimmableBackground = Background; + ApplyToBackground(b => + { + if (epilepsyWarning != null) + epilepsyWarning.DimmableBackground = b; + + b?.FadeColour(Color4.White, 800, Easing.OutQuint); + }); + Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment); content.ScaleTo(0.7f); - Background?.FadeColour(Color4.White, 800, Easing.OutQuint); contentIn(); @@ -225,7 +230,8 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - Background.EnableUserDim.Value = false; + ApplyToBackground(b => b.EnableUserDim.Value = false); + BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -270,16 +276,22 @@ namespace osu.Game.Screens.Play if (inputManager.HoveredDrawables.Contains(VisualSettings)) { // Preview user-defined background dim and blur when hovered on the visual settings panel. - Background.EnableUserDim.Value = true; - Background.BlurAmount.Value = 0; + ApplyToBackground(b => + { + b.EnableUserDim.Value = true; + b.BlurAmount.Value = 0; + }); BackgroundBrightnessReduction = false; } else { - // Returns background dim and blur to the values specified by PlayerLoader. - Background.EnableUserDim.Value = false; - Background.BlurAmount.Value = BACKGROUND_BLUR; + ApplyToBackground(b => + { + // Returns background dim and blur to the values specified by PlayerLoader. + b.EnableUserDim.Value = false; + b.BlurAmount.Value = BACKGROUND_BLUR; + }); BackgroundBrightnessReduction = true; } diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 8eb253608b..88dab88d42 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Screens.Backgrounds; namespace osu.Game.Screens.Play @@ -9,6 +10,6 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value); - public new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background; + public void ApplyToBackground(Action action) => base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b)); } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 98794627fe..c1f5d92d17 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -18,7 +18,6 @@ using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Statistics; using osuTK; @@ -235,15 +234,18 @@ namespace osu.Game.Screens.Ranking { base.OnEntering(last); - ((BackgroundScreenBeatmap)Background).BlurAmount.Value = BACKGROUND_BLUR; + ApplyToBackground(b => + { + b.BlurAmount.Value = BACKGROUND_BLUR; + b.FadeTo(0.5f, 250); + }); - Background.FadeTo(0.5f, 250); bottomPanel.FadeTo(1, 250); } public override bool OnExiting(IScreen next) { - Background.FadeTo(1, 250); + ApplyToBackground(b => b.FadeTo(1, 250)); return base.OnExiting(next); } @@ -293,7 +295,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = false; // Dim background. - Background.FadeTo(0.1f, 150); + ApplyToBackground(b => b.FadeTo(0.1f, 150)); detachedPanel = expandedPanel; } @@ -317,7 +319,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = true; // Un-dim background. - Background.FadeTo(0.5f, 150); + ApplyToBackground(b => b.FadeTo(0.5f, 150)); detachedPanel = null; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 50a1dd6edf..3c255011c9 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -19,7 +19,6 @@ using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Select.Options; @@ -38,6 +37,7 @@ using osu.Game.Collections; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; using System.Diagnostics; +using osu.Game.Screens.Play; namespace osu.Game.Screens.Select { @@ -682,12 +682,12 @@ namespace osu.Game.Screens.Select /// The working beatmap. private void updateComponentFromBeatmap(WorkingBeatmap beatmap) { - if (Background is BackgroundScreenBeatmap backgroundModeBeatmap) + ApplyToBackground(backgroundModeBeatmap => { backgroundModeBeatmap.Beatmap = beatmap; backgroundModeBeatmap.BlurAmount.Value = BACKGROUND_BLUR; backgroundModeBeatmap.FadeColour(Color4.White, 250); - } + }); beatmapInfoWedge.Beatmap = beatmap; From 5904e426ebf666ce8191ec5902389adc9f25b1b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:00:25 +0900 Subject: [PATCH 092/198] Remove unused variable --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index b5df3285f0..7ade7725d9 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -323,14 +323,12 @@ namespace osu.Game.Tests.Visual.Background private class FadeAccessibleResults : ResultsScreen { - private FadeAccessibleBackground background; - public FadeAccessibleResults(ScoreInfo score) : base(score, true) { } - protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value); + protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR); } From 57a8cd74615765e54d60a425f904a4034045b1a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:17:55 +0900 Subject: [PATCH 093/198] Schedule deselection operations for safety --- osu.Game/Overlays/Mods/ModSection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index d00a365dde..47b101c2b0 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(IEnumerable modTypes, bool immediate = false) + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) => Schedule(() => { int delay = 0; @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods } } } - } + }); /// /// Select one or more mods in this section and deselects all other ones. From 9bac791a576a0b16860f3addb3ef3a451d166d1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:17:42 +0900 Subject: [PATCH 094/198] Fix deselection of autoplay mod failing --- osu.Game/Screens/Select/PlaySongSelect.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 50a61ed4c2..e61d5cce85 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -9,6 +9,7 @@ using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.Select protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + private ModAutoplay getAutoplayMod() => Ruleset.Value.CreateInstance().GetAutoplayMod(); + public override void OnResuming(IScreen last) { base.OnResuming(last); @@ -50,10 +53,10 @@ namespace osu.Game.Screens.Select if (removeAutoModOnResume) { - var autoType = Ruleset.Value.CreateInstance().GetAutoplayMod()?.GetType(); + var autoType = getAutoplayMod()?.GetType(); if (autoType != null) - ModSelect.DeselectTypes(new[] { autoType }, true); + Mods.Value = Mods.Value.Where(m => m.GetType() != autoType).ToArray(); removeAutoModOnResume = false; } @@ -81,12 +84,9 @@ namespace osu.Game.Screens.Select // Ctrl+Enter should start map with autoplay enabled. if (GetContainingInputManager().CurrentState?.Keyboard.ControlPressed == true) { - var auto = Ruleset.Value.CreateInstance().GetAutoplayMod(); - var autoType = auto?.GetType(); + var autoplayMod = getAutoplayMod(); - var mods = Mods.Value; - - if (autoType == null) + if (autoplayMod == null) { notifications?.Post(new SimpleNotification { @@ -95,9 +95,11 @@ namespace osu.Game.Screens.Select return false; } - if (mods.All(m => m.GetType() != autoType)) + var mods = Mods.Value; + + if (mods.All(m => m.GetType() != autoplayMod.GetType())) { - Mods.Value = mods.Append(auto).ToArray(); + Mods.Value = mods.Append(autoplayMod).ToArray(); removeAutoModOnResume = true; } } From 4d6c13f169cc7c09c4db61bbfdfd780c5b44fef5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:18:13 +0900 Subject: [PATCH 095/198] Privatise ModSelectOverlay methods that may be unsafe to be called externally --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 34f5c70adb..491052fa2c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -249,7 +249,7 @@ namespace osu.Game.Overlays.Mods { Width = 180, Text = "Deselect All", - Action = DeselectAll, + Action = deselectAll, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, @@ -318,7 +318,7 @@ namespace osu.Game.Overlays.Mods sampleOff = audio.Samples.Get(@"UI/check-off"); } - public void DeselectAll() + private void deselectAll() { foreach (var section in ModSectionsContainer.Children) section.DeselectAll(); @@ -331,7 +331,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(Type[] modTypes, bool immediate = false) + private void deselectTypes(Type[] modTypes, bool immediate = false) { if (modTypes.Length == 0) return; @@ -438,7 +438,7 @@ namespace osu.Game.Overlays.Mods { if (State.Value == Visibility.Visible) sampleOn?.Play(); - DeselectTypes(selectedMod.IncompatibleMods, true); + deselectTypes(selectedMod.IncompatibleMods, true); if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show(); } From 5d8c153c1e21949156a0dbf54b9cf8d71bca4c4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 16:41:03 +0900 Subject: [PATCH 096/198] Move schedule logic to buttons rather than section It turns out there's some quite convoluted scheduling / order of execution requirements of ModSelectOverlay and ModSection. Applying scheduling causes a runaway condition ending in zero frames after many mod button changes. I wanted to avoid rewriting the whole component, so have just moved the schedule to guard against the part where drawables are actually changed. --- osu.Game/Overlays/Mods/ModButton.cs | 62 +++++++++++++++------------- osu.Game/Overlays/Mods/ModSection.cs | 8 ++-- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index e574828cd2..51c8bcf9a9 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -64,41 +64,45 @@ namespace osu.Game.Overlays.Mods if (newIndex >= 0 && !Mods[newIndex].HasImplementation) return false; - selectedIndex = newIndex; - Mod modAfter = SelectedMod ?? Mods[0]; - - if (beforeSelected != Selected) + Schedule(() => { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); - } + selectedIndex = newIndex; + Mod modAfter = SelectedMod ?? Mods[0]; - if (modBefore != modAfter) - { - const float rotate_angle = 16; - - foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - - backgroundIcon.Mod = modAfter; - - using (BeginDelayedSequence(mod_switch_duration, true)) + if (beforeSelected != Selected) { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => displayMod(modAfter)); + iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); + iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); } - } - foregroundIcon.Selected.Value = Selected; + if (modBefore != modAfter) + { + const float rotate_angle = 16; + + foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); + backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); + + backgroundIcon.Mod = modAfter; + + using (BeginDelayedSequence(mod_switch_duration, true)) + { + foregroundIcon + .RotateTo(-rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + backgroundIcon + .RotateTo(rotate_angle * direction) + .RotateTo(0f, mod_switch_duration, mod_switch_easing); + + Schedule(() => displayMod(modAfter)); + } + } + + foregroundIcon.Selected.Value = Selected; + + SelectionChanged?.Invoke(SelectedMod); + }); - SelectionChanged?.Invoke(SelectedMod); return true; } diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 47b101c2b0..0107f94dcf 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Mods /// /// The types of s which should be deselected. /// Set to true to bypass animations and update selections immediately. - public void DeselectTypes(IEnumerable modTypes, bool immediate = false) => Schedule(() => + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) { int delay = 0; @@ -124,13 +124,13 @@ namespace osu.Game.Overlays.Mods } } } - }); + } /// /// Select one or more mods in this section and deselects all other ones. /// /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) => Schedule(() => + public void SelectTypes(IEnumerable modTypes) { foreach (var button in buttons) { @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods else button.Deselect(); } - }); + } protected ModSection() { From 54982dcdd7e46ee8081f176ebc07b9037e0bfd5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Jan 2021 22:42:39 +0900 Subject: [PATCH 097/198] Refactor LoadingLayer to avoid applying effects to external drawables In theory this seemed like a good idea (and an optimisation in some cases, due to lower fill rate), but in practice this leads to weird edge cases. This aims to do away with the operations on external drawables by applying a dim to the area behind the `LoadingLayer` when required. I went over each usage and ensured they look as good or better than previously. The specific bad usage here was the restoration of the colour on dispose (if the `LoadingLayer` was disposed in a still-visible state). I'm aware that the `BeatmapListingOverlay` will now dim completely during load. I think this is fine for the time being. --- .../UserInterface/TestSceneLoadingLayer.cs | 10 +- .../Graphics/UserInterface/LoadingLayer.cs | 29 +- .../Overlays/AccountCreation/ScreenEntry.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 8 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- .../BeatmapSet/Scores/ScoresContainer.cs | 10 +- .../Dashboard/Friends/FriendDisplay.cs | 2 +- osu.Game/Overlays/DashboardOverlay.cs | 2 +- osu.Game/Overlays/NewsOverlay.cs | 2 +- .../Overlays/Rankings/SpotlightsLayout.cs | 3 +- osu.Game/Overlays/RankingsOverlay.cs | 6 +- .../OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- .../Match/MultiplayerMatchSettingsOverlay.cs | 279 ++++++------ .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- .../Multiplayer/MultiplayerPlayer.cs | 2 +- .../PlaylistsMatchSettingsOverlay.cs | 409 +++++++++--------- .../Screens/Play/BeatmapMetadataDisplay.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 6 +- 18 files changed, 389 insertions(+), 389 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index 1be191fc29..a694595115 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.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.Framework.Graphics.Containers; @@ -14,11 +15,12 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLoadingLayer : OsuTestScene { - private Drawable dimContent; private LoadingLayer overlay; private Container content; + private Drawable dimContent => overlay.Children.OfType().First(); + [SetUp] public void SetUp() => Schedule(() => { @@ -29,14 +31,14 @@ namespace osu.Game.Tests.Visual.UserInterface Size = new Vector2(300), Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new[] + Children = new Drawable[] { new Box { Colour = Color4.SlateGray, RelativeSizeAxes = Axes.Both, }, - dimContent = new FillFlowContainer + new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -51,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface new TriangleButton { Text = "puush me", Width = 200, Action = () => { } }, } }, - overlay = new LoadingLayer(dimContent), + overlay = new LoadingLayer(true), } }, }; diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index c8c4424bee..6aca30328a 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osuTK; using osuTK.Graphics; @@ -17,22 +18,31 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - private readonly Drawable dimTarget; + private readonly Box backgroundDimLayer; /// - /// Constuct a new loading spinner. + /// Construct a new loading spinner. /// - /// An optional target to dim when displayed. + /// Whether the full background area should be dimmed while loading. /// Whether the spinner should have a surrounding black box for visibility. - public LoadingLayer(Drawable dimTarget = null, bool withBox = true) + public LoadingLayer(bool dimBackground = false, bool withBox = true) : base(withBox) { RelativeSizeAxes = Axes.Both; Size = new Vector2(1); - this.dimTarget = dimTarget; - MainContents.RelativeSizeAxes = Axes.None; + + if (dimBackground) + { + AddInternal(backgroundDimLayer = new Box + { + Depth = float.MaxValue, + Colour = Color4.Black, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }); + } } public override bool HandleNonPositionalInput => false; @@ -56,19 +66,20 @@ namespace osu.Game.Graphics.UserInterface protected override void PopIn() { - dimTarget?.FadeColour(OsuColour.Gray(0.5f), TRANSITION_DURATION, Easing.OutQuint); + backgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); base.PopIn(); } protected override void PopOut() { - dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); + backgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); base.PopOut(); } protected override void Update() { base.Update(); + MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 30, 100)); } @@ -79,7 +90,7 @@ namespace osu.Game.Graphics.UserInterface if (State.Value == Visibility.Visible) { // ensure we don't leave the target in a bad state. - dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); + // dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index a0b1b27ebf..0d5538155a 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.AccountCreation }, }, }, - loadingLayer = new LoadingLayer(mainContent) + loadingLayer = new LoadingLayer(true) }; textboxes = new[] { usernameTextBox, emailTextBox, passwordTextBox }; diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 1e29e713af..0c9c995dd6 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -92,14 +92,14 @@ namespace osu.Game.Overlays { foundContent = new FillFlowContainer(), notFoundContent = new NotFoundDrawable(), - loadingLayer = new LoadingLayer(panelTarget) } } - } + }, }, } - } - } + }, + }, + loadingLayer = new LoadingLayer(true) }; } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index c983b337b5..7ad6906cea 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Size = new Vector2(18), Shadow = false, }, - loading = new LoadingLayer(icon, false), + loading = new LoadingLayer(true, false), }); Action = () => diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 9a2dcd014a..b598b7d97f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -157,11 +157,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores } } }, - loading = new LoadingLayer() } } - } - } + }, + }, + loading = new LoadingLayer() }); } @@ -228,7 +228,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { Scores = null; notSupporterPlaceholder.Show(); + loading.Hide(); + loading.FinishTransforms(); return; } @@ -241,6 +243,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores getScoresRequest.Success += scores => { loading.Hide(); + loading.FinishTransforms(); + Scores = scores; if (!scores.Scores.Any()) diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs index cc26a11da1..e6fe6ac749 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs @@ -128,7 +128,7 @@ namespace osu.Game.Overlays.Dashboard.Friends AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 50 } }, - loading = new LoadingLayer(itemsPlaceholder) + loading = new LoadingLayer(true) } } } diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 04defce636..03c320debe 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays } } }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index c8c1db012f..5820d405d4 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays }, }, }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 61339df76f..b16e0a4908 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.Rankings { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + InternalChild = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Vertical = 10 } }, - loading = new LoadingLayer(content) + loading = new LoadingLayer(true) } } } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index ae6d49960a..25350e310a 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -42,6 +42,8 @@ namespace osu.Game.Overlays Depth = -float.MaxValue }) { + loading = new LoadingLayer(true); + Children = new Drawable[] { background = new Box @@ -74,12 +76,12 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Margin = new MarginPadding { Bottom = 10 } }, - loading = new LoadingLayer(contentContainer), } } } } - } + }, + loading }; } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 79f5dfdee1..0f06188dc2 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -65,7 +65,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Padding = new MarginPadding(10), Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } }, - loadingLayer = new LoadingLayer(roomsContainer), + loadingLayer = new LoadingLayer(true), } }, new RoomInspector diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs index ae03d384f6..67c6aa7add 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerMatchSettingsOverlay.cs @@ -71,201 +71,192 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [BackgroundDependencyLoader] private void load(OsuColour colours) { - Container dimContent; - InternalChildren = new Drawable[] { - dimContent = new Container + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - new Box + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d"), - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + new OsuScrollContainer { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Padding = new MarginPadding { - new OsuScrollContainer + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 + }, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new FillFlowContainer { - Padding = new MarginPadding + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] { - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10 - }, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new FillFlowContainer + new Container { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), Children = new Drawable[] { - new Container + new SectionContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, + Children = new[] { - new SectionContainer + new Section("Room name") { - Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, - Children = new[] + Child = NameField = new SettingsTextBox { - new Section("Room name") + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + }, + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] { - Child = NameField = new SettingsTextBox + TypePicker = new GameTypePicker { RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - }, - }, - new Section("Room visibility") - { - Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker - { Enabled = { Value = false } }, - }, - new Section("Game type") - { - Alpha = disabled_alpha, - Child = new FillFlowContainer + typeLabel = new OsuSpriteText { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(7), - Children = new Drawable[] - { - TypePicker = new GameTypePicker - { - RelativeSizeAxes = Axes.X, - Enabled = { Value = false } - }, - typeLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Colour = colours.Yellow - }, - }, + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow }, }, }, }, - new SectionContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Max participants") - { - Alpha = disabled_alpha, - Child = MaxParticipantsField = new SettingsNumberTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - new Section("Password (optional)") - { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - } - } }, }, - initialBeatmapControl = new BeatmapSelectionControl + new SectionContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Width = 0.5f + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + } } - } + }, + }, + initialBeatmapControl = new BeatmapSelectionControl + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Width = 0.5f } - }, - }, + } + } }, - new Drawable[] + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - new Container + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = 2, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, Children = new Drawable[] { - new Box + ApplyButton = new CreateOrUpdateButton { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, }, - new FillFlowContainer + ErrorText = new OsuSpriteText { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding { Vertical = 20 }, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - ApplyButton = new CreateOrUpdateButton - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(230, 55), - Enabled = { Value = false }, - Action = apply, - }, - ErrorText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Alpha = 0, - Depth = 1, - Colour = colours.RedDark - } - } + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark } } } } } - }, + } } }, - loadingLayer = new LoadingLayer(dimContent) + loadingLayer = new LoadingLayer(true) }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 72539a2e3a..36dbb9e792 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -47,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - AddInternal(loadingLayer = new LoadingLayer(Carousel)); + AddInternal(loadingLayer = new LoadingLayer(true)); initialBeatmap = Beatmap.Value; initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 4247e954bd..4bee502e2e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add); - HUDOverlay.Add(loadingDisplay = new LoadingLayer(DrawableRuleset) { Depth = float.MaxValue }); + HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); if (Token == null) return; // Todo: Somehow handle token retrieval failure. diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 6b92526f35..01f9920609 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -64,243 +64,234 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [BackgroundDependencyLoader] private void load(OsuColour colours) { - Container dimContent; - InternalChildren = new Drawable[] { - dimContent = new Container + new Box { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + Colour = Color4Extensions.FromHex(@"28242d"), + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - new Box + new Dimension(GridSizeMode.Distributed), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d"), - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - RowDimensions = new[] + new OsuScrollContainer { - new Dimension(GridSizeMode.Distributed), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] + Padding = new MarginPadding { - new OsuScrollContainer - { - Padding = new MarginPadding - { - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10 - }, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new Container - { - Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SectionContainer - { - Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Room name") - { - Child = NameField = new SettingsTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - LengthLimit = 100 - }, - }, - new Section("Duration") - { - Child = DurationField = new DurationDropdown - { - RelativeSizeAxes = Axes.X, - Items = new[] - { - TimeSpan.FromMinutes(30), - TimeSpan.FromHours(1), - TimeSpan.FromHours(2), - TimeSpan.FromHours(4), - TimeSpan.FromHours(8), - TimeSpan.FromHours(12), - //TimeSpan.FromHours(16), - TimeSpan.FromHours(24), - TimeSpan.FromDays(3), - TimeSpan.FromDays(7) - } - } - }, - new Section("Room visibility") - { - Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker - { - Enabled = { Value = false } - }, - }, - new Section("Game type") - { - Alpha = disabled_alpha, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Direction = FillDirection.Vertical, - Spacing = new Vector2(7), - Children = new Drawable[] - { - TypePicker = new GameTypePicker - { - RelativeSizeAxes = Axes.X, - Enabled = { Value = false } - }, - typeLabel = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 14), - Colour = colours.Yellow - }, - }, - }, - }, - new Section("Max participants") - { - Alpha = disabled_alpha, - Child = MaxParticipantsField = new SettingsNumberTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - new Section("Password (optional)") - { - Alpha = disabled_alpha, - Child = new SettingsPasswordTextBox - { - RelativeSizeAxes = Axes.X, - TabbableContentContainer = this, - ReadOnly = true, - }, - }, - }, - }, - new SectionContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, - Children = new[] - { - new Section("Playlist") - { - Child = new GridContainer - { - RelativeSizeAxes = Axes.X, - Height = 300, - Content = new[] - { - new Drawable[] - { - playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } - }, - new Drawable[] - { - playlistLength = new OsuSpriteText - { - Margin = new MarginPadding { Vertical = 5 }, - Colour = colours.Yellow, - Font = OsuFont.GetFont(size: 12), - } - }, - new Drawable[] - { - new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Height = 40, - Text = "Edit playlist", - Action = () => EditPlaylist?.Invoke() - } - } - }, - RowDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - } - } - }, - }, - }, - }, - } - }, - }, + Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10 }, - new Drawable[] + RelativeSizeAxes = Axes.Both, + Children = new[] { new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Y = 2, + Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new Box + new SectionContainer { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Margin = new MarginPadding { Vertical = 20 }, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] + Padding = new MarginPadding { Right = FIELD_PADDING / 2 }, + Children = new[] { - ApplyButton = new CreateRoomButton + new Section("Room name") { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(230, 55), - Enabled = { Value = false }, - Action = apply, + Child = NameField = new SettingsTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + LengthLimit = 100 + }, }, - ErrorText = new OsuSpriteText + new Section("Duration") { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Alpha = 0, - Depth = 1, - Colour = colours.RedDark - } - } + Child = DurationField = new DurationDropdown + { + RelativeSizeAxes = Axes.X, + Items = new[] + { + TimeSpan.FromMinutes(30), + TimeSpan.FromHours(1), + TimeSpan.FromHours(2), + TimeSpan.FromHours(4), + TimeSpan.FromHours(8), + TimeSpan.FromHours(12), + //TimeSpan.FromHours(16), + TimeSpan.FromHours(24), + TimeSpan.FromDays(3), + TimeSpan.FromDays(7) + } + } + }, + new Section("Room visibility") + { + Alpha = disabled_alpha, + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, + }, + new Section("Game type") + { + Alpha = disabled_alpha, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(7), + Children = new Drawable[] + { + TypePicker = new GameTypePicker + { + RelativeSizeAxes = Axes.X, + Enabled = { Value = false } + }, + typeLabel = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 14), + Colour = colours.Yellow + }, + }, + }, + }, + new Section("Max participants") + { + Alpha = disabled_alpha, + Child = MaxParticipantsField = new SettingsNumberTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + new Section("Password (optional)") + { + Alpha = disabled_alpha, + Child = new SettingsPasswordTextBox + { + RelativeSizeAxes = Axes.X, + TabbableContentContainer = this, + ReadOnly = true, + }, + }, + }, + }, + new SectionContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Padding = new MarginPadding { Left = FIELD_PADDING / 2 }, + Children = new[] + { + new Section("Playlist") + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + Height = 300, + Content = new[] + { + new Drawable[] + { + playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } + }, + new Drawable[] + { + playlistLength = new OsuSpriteText + { + Margin = new MarginPadding { Vertical = 5 }, + Colour = colours.Yellow, + Font = OsuFont.GetFont(size: 12), + } + }, + new Drawable[] + { + new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Height = 40, + Text = "Edit playlist", + Action = () => EditPlaylist?.Invoke() + } + } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + } + } + }, + }, + }, + }, + } + }, + }, + }, + new Drawable[] + { + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Y = 2, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex(@"28242d").Darken(0.5f).Opacity(1f), + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Margin = new MarginPadding { Vertical = 20 }, + Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, + Children = new Drawable[] + { + ApplyButton = new CreateRoomButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(230, 55), + Enabled = { Value = false }, + Action = apply, + }, + ErrorText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Alpha = 0, + Depth = 1, + Colour = colours.RedDark } } } } } - }, + } } }, - loadingLayer = new LoadingLayer(dimContent) + loadingLayer = new LoadingLayer(true) }; TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue?.Name ?? string.Empty, true); diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 5530b4beac..00176e9127 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -131,7 +131,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, FillMode = FillMode.Fill, }, - loading = new LoadingLayer(backgroundSprite) + loading = new LoadingLayer(true) } }, new OsuSpriteText diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 71f78c5c95..8a1c291fca 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -63,8 +63,6 @@ namespace osu.Game.Screens.Select public BeatmapDetails() { - Container content; - Children = new Drawable[] { new Box @@ -72,7 +70,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Colour = Color4.Black.Opacity(0.5f), }, - content = new Container + new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = spacing }, @@ -159,7 +157,7 @@ namespace osu.Game.Screens.Select }, }, }, - loading = new LoadingLayer(content), + loading = new LoadingLayer(true), }; } From 0b1ee2e267f3daea22a437d445edf4336bd048f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 17:42:19 +0900 Subject: [PATCH 098/198] Remove unused dispose logic --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 6aca30328a..a4905156a9 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osuTK; @@ -82,16 +81,5 @@ namespace osu.Game.Graphics.UserInterface MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 30, 100)); } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (State.Value == Visibility.Visible) - { - // ensure we don't leave the target in a bad state. - // dimTarget?.FadeColour(Color4.White, TRANSITION_DURATION, Easing.OutQuint); - } - } } } From 0639429a23bb706d84b56dea98b7f23d3f4563b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:10:39 +0900 Subject: [PATCH 099/198] Fix test (and remove no longer valid test) --- .../UserInterface/TestSceneLoadingLayer.cs | 36 ++++++++----------- .../Graphics/UserInterface/LoadingLayer.cs | 8 ++--- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index a694595115..d426723f0b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs @@ -1,11 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; @@ -15,12 +15,10 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneLoadingLayer : OsuTestScene { - private LoadingLayer overlay; + private TestLoadingLayer overlay; private Container content; - private Drawable dimContent => overlay.Children.OfType().First(); - [SetUp] public void SetUp() => Schedule(() => { @@ -53,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface new TriangleButton { Text = "puush me", Width = 200, Action = () => { } }, } }, - overlay = new LoadingLayer(true), + overlay = new TestLoadingLayer(true), } }, }; @@ -66,25 +64,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("show", () => overlay.Show()); - AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White); + AddUntilStep("wait for content dim", () => overlay.BackgroundDimLayer.Alpha > 0); AddStep("hide", () => overlay.Hide()); - AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White); - } - - [Test] - public void TestContentRestoreOnDispose() - { - AddAssert("not visible", () => !overlay.IsPresent); - - AddStep("show", () => overlay.Show()); - - AddUntilStep("wait for content dim", () => dimContent.Colour != Color4.White); - - AddStep("expire", () => overlay.Expire()); - - AddUntilStep("wait for content restore", () => dimContent.Colour == Color4.White); + AddUntilStep("wait for content restore", () => Precision.AlmostEquals(overlay.BackgroundDimLayer.Alpha, 0)); } [Test] @@ -100,5 +84,15 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("hide", () => overlay.Hide()); } + + private class TestLoadingLayer : LoadingLayer + { + public new Box BackgroundDimLayer => base.BackgroundDimLayer; + + public TestLoadingLayer(bool dimBackground = false, bool withBox = true) + : base(dimBackground, withBox) + { + } + } } } diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index a4905156a9..3e3fd32d65 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - private readonly Box backgroundDimLayer; + protected Box BackgroundDimLayer { get; private set; } /// /// Construct a new loading spinner. @@ -34,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface if (dimBackground) { - AddInternal(backgroundDimLayer = new Box + AddInternal(BackgroundDimLayer = new Box { Depth = float.MaxValue, Colour = Color4.Black, @@ -65,13 +65,13 @@ namespace osu.Game.Graphics.UserInterface protected override void PopIn() { - backgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); + BackgroundDimLayer?.FadeTo(0.5f, TRANSITION_DURATION * 2, Easing.OutQuint); base.PopIn(); } protected override void PopOut() { - backgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); + BackgroundDimLayer?.FadeOut(TRANSITION_DURATION, Easing.OutQuint); base.PopOut(); } From d0d2e41b28df281507e16d5de19f4ec0c5e8a8b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:19:28 +0900 Subject: [PATCH 100/198] Fix display settings binding to configuration bindables in async load --- .../Settings/Sections/Graphics/LayoutSettings.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 3d3b543d70..e815e2f68b 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -133,6 +133,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; + scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); + windowModes.BindCollectionChanged((sender, args) => { if (windowModes.Count > 1) @@ -141,8 +150,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics windowModeDropdown.Hide(); }, true); - windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); - currentDisplay.BindValueChanged(display => Schedule(() => { resolutions.RemoveRange(1, resolutions.Count - 1); @@ -159,8 +166,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics updateResolutionDropdown(); }), true); - scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); - scalingMode.BindValueChanged(mode => { scalingSettings.ClearTransforms(); From 83dbba3cbfb3a02297ae8bc801a11bf2028f3cb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 18:41:45 +0900 Subject: [PATCH 101/198] Fix carousel beatmap set panels applying transforms to difficulties while they are loading --- .../Carousel/DrawableCarouselBeatmapSet.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index b3c5d458d6..17fa66447d 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -40,6 +41,8 @@ namespace osu.Game.Screens.Select.Carousel private BeatmapSetInfo beatmapSet; + private Task beatmapsLoadTask; + [Resolved] private BeatmapManager manager { get; set; } @@ -85,7 +88,9 @@ namespace osu.Game.Screens.Select.Carousel base.UpdateItem(); Content.Clear(); + beatmapContainer = null; + beatmapsLoadTask = null; if (Item == null) return; @@ -122,11 +127,7 @@ namespace osu.Game.Screens.Select.Carousel MovementContainer.MoveToX(0, 500, Easing.OutExpo); - if (beatmapContainer != null) - { - foreach (var beatmap in beatmapContainer) - beatmap.MoveToY(0, 800, Easing.OutQuint); - } + updateBeatmapYPositions(); } protected override void Selected() @@ -163,7 +164,7 @@ namespace osu.Game.Screens.Select.Carousel ChildrenEnumerable = visibleBeatmaps.Select(c => c.CreateDrawableRepresentation()) }; - LoadComponentAsync(beatmapContainer, loaded => + beatmapsLoadTask = LoadComponentAsync(beatmapContainer, loaded => { // make sure the pooled target hasn't changed. if (beatmapContainer != loaded) @@ -173,16 +174,26 @@ namespace osu.Game.Screens.Select.Carousel updateBeatmapYPositions(); }); } + } - void updateBeatmapYPositions() + private void updateBeatmapYPositions() + { + if (beatmapsLoadTask == null || !beatmapsLoadTask.IsCompleted) + return; + + float yPos = DrawableCarouselBeatmap.CAROUSEL_BEATMAP_SPACING; + + bool isSelected = Item.State.Value == CarouselItemState.Selected; + + foreach (var panel in beatmapContainer.Children) { - float yPos = DrawableCarouselBeatmap.CAROUSEL_BEATMAP_SPACING; - - foreach (var panel in beatmapContainer.Children) + if (isSelected) { panel.MoveToY(yPos, 800, Easing.OutQuint); yPos += panel.Item.TotalHeight; } + else + panel.MoveToY(0, 800, Easing.OutQuint); } } From 4b539b01c1862bfb0a29ad69dc6b400f3719e99b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Jan 2021 20:38:58 +0900 Subject: [PATCH 102/198] Match code between updateSelectedBeatmap/Ruleset --- osu.Game/Screens/Select/SongSelect.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e3036c662b..7e217ca7a4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -435,13 +435,15 @@ namespace osu.Game.Screens.Select return; beatmapNoDebounce = beatmap; - performUpdateSelected(); } private void updateSelectedRuleset(RulesetInfo ruleset) { - if (ruleset == null || ruleset.Equals(rulesetNoDebounce)) + if (ruleset == null && rulesetNoDebounce == null) + return; + + if (ruleset?.Equals(rulesetNoDebounce) == true) return; rulesetNoDebounce = ruleset; From 2b253f6d01426901994e95b03ead4233f7cb67dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:39:59 +0100 Subject: [PATCH 103/198] Remove now-unused fields & locals --- osu.Game/Overlays/AccountCreation/ScreenEntry.cs | 4 +--- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 0d5538155a..bcb3d4b635 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -48,11 +48,9 @@ namespace osu.Game.Overlays.AccountCreation [BackgroundDependencyLoader] private void load(OsuColour colours) { - FillFlowContainer mainContent; - InternalChildren = new Drawable[] { - mainContent = new FillFlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 00176e9127..b53141e8fb 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -53,7 +53,6 @@ namespace osu.Game.Screens.Play private readonly Bindable> mods; private readonly Drawable facade; private LoadingSpinner loading; - private Sprite backgroundSprite; public IBindable> Mods => mods; @@ -123,7 +122,7 @@ namespace osu.Game.Screens.Play Masking = true, Children = new Drawable[] { - backgroundSprite = new Sprite + new Sprite { RelativeSizeAxes = Axes.Both, Texture = beatmap?.Background, From ac1d6d444430296d2ab08ac3f5e89f5a965655ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:40:16 +0100 Subject: [PATCH 104/198] Make auto-property get-only --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 3e3fd32d65..d86ea9e4ea 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { - protected Box BackgroundDimLayer { get; private set; } + protected Box BackgroundDimLayer { get; } /// /// Construct a new loading spinner. From 0880e76da8d58d02ffe4ad95c173d9373e3d50b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Jan 2021 22:47:57 +0100 Subject: [PATCH 105/198] Mark background dim layer as possibly-null --- osu.Game/Graphics/UserInterface/LoadingLayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index d86ea9e4ea..47ba5fce4d 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; @@ -17,6 +18,7 @@ namespace osu.Game.Graphics.UserInterface /// public class LoadingLayer : LoadingSpinner { + [CanBeNull] protected Box BackgroundDimLayer { get; } /// From 15dd7a87a62d73d0ba6e85fea034f1d7cb9656c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:19:12 +0900 Subject: [PATCH 106/198] Move gameplay preview event binding to LoadComplete --- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index e815e2f68b..7acbf038d8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -132,14 +132,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } }, }; - - scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); } protected override void LoadComplete() { base.LoadComplete(); + scalingSettings.ForEach(s => bindPreviewEvent(s.Current)); + windowModeDropdown.Current.ValueChanged += _ => updateResolutionDropdown(); windowModes.BindCollectionChanged((sender, args) => @@ -186,11 +186,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } } - /// - /// Create a delayed bindable which only updates when a condition is met. - /// - /// The config bindable. - /// A bindable which will propagate updates with a delay. private void bindPreviewEvent(Bindable bindable) { bindable.ValueChanged += _ => From 11a0c637bc07812e01125df31251791cf9a24694 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:25:53 +0900 Subject: [PATCH 107/198] Mark background properties as nullable --- osu.Game/Screens/OsuScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index c97c0aef2b..d1cdb9f1de 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -119,8 +120,10 @@ namespace osu.Game.Screens /// /// The background created and owned by this screen. May be null if the background didn't change. /// + [CanBeNull] private BackgroundScreen ownedBackground; + [CanBeNull] private BackgroundScreen background; [Resolved(canBeNull: true)] From e9d4e4d1d5bb5c6df13474c867c81b4d516cd61d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:26:13 +0900 Subject: [PATCH 108/198] Add xmldoc and throw a local exception on null background --- osu.Game/Screens/BackgroundScreen.cs | 4 ++++ osu.Game/Screens/OsuScreen.cs | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index ea220c2f82..c81362eebe 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -34,6 +34,10 @@ namespace osu.Game.Screens return false; } + /// + /// Apply arbitrary changes to this background in a thread safe manner. + /// + /// The operation to perform. public void ApplyToBackground(Action action) => Schedule(() => action.Invoke(this)); protected override void Update() diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index d1cdb9f1de..11467db6c8 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -115,8 +115,6 @@ namespace osu.Game.Screens Mods = screenDependencies.Mods; } - public void ApplyToBackground(Action action) => background.ApplyToBackground(action); - /// /// The background created and owned by this screen. May be null if the background didn't change. /// @@ -148,6 +146,18 @@ namespace osu.Game.Screens Activity.Value ??= InitialActivity; } + /// + /// Apply arbitrary changes to the current background screen in a thread safe manner. + /// + /// The operation to perform. + public void ApplyToBackground(Action action) + { + if (background == null) + throw new InvalidOperationException("Attempted to apply to background before screen is pushed"); + + background.ApplyToBackground(action); + } + public override void OnResuming(IScreen last) { if (PlayResumeSound) From 550ef3f13307f3d8592a0bc1ecd103f67af046e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 15:28:01 +0900 Subject: [PATCH 109/198] Aggressively dispose ownedBackground if it was not used, because we can --- osu.Game/Screens/OsuScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 11467db6c8..fb26739a44 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -185,6 +185,7 @@ namespace osu.Game.Screens if (background != ownedBackground) { // background may have not been replaced, at which point we don't want to track the background lifetime. + ownedBackground?.Dispose(); ownedBackground = null; } From 07cff7038728545a5ce399c3b727b1d03c68813c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 18:19:03 +0900 Subject: [PATCH 110/198] Add specific messaging for when there's no background stack available --- osu.Game/Screens/OsuScreen.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index fb26739a44..e1a29946f4 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -125,6 +125,7 @@ namespace osu.Game.Screens private BackgroundScreen background; [Resolved(canBeNull: true)] + [CanBeNull] private BackgroundScreenStack backgroundStack { get; set; } [Resolved(canBeNull: true)] @@ -152,8 +153,11 @@ namespace osu.Game.Screens /// The operation to perform. public void ApplyToBackground(Action action) { + if (backgroundStack == null) + throw new InvalidOperationException("Attempted to apply to background without a background stack being available."); + if (background == null) - throw new InvalidOperationException("Attempted to apply to background before screen is pushed"); + throw new InvalidOperationException("Attempted to apply to background before screen is pushed."); background.ApplyToBackground(action); } From 99701a6d9b22e2fbc4864556a14f9f44311b5236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 21:06:33 +0900 Subject: [PATCH 111/198] Add null check on beatmapContainer for safety --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 17fa66447d..c0415384c8 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -178,6 +178,9 @@ namespace osu.Game.Screens.Select.Carousel private void updateBeatmapYPositions() { + if (beatmapContainer == null) + return; + if (beatmapsLoadTask == null || !beatmapsLoadTask.IsCompleted) return; From 43b9fde45731fdbd5da77f119eaedc5e0afa0f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 13:15:15 +0100 Subject: [PATCH 112/198] Add some nullability annotations for good measure --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index c0415384c8..d7e901b71e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -37,10 +38,12 @@ namespace osu.Game.Screens.Select.Carousel public IEnumerable DrawableBeatmaps => beatmapContainer?.Children ?? Enumerable.Empty(); + [CanBeNull] private Container beatmapContainer; private BeatmapSetInfo beatmapSet; + [CanBeNull] private Task beatmapsLoadTask; [Resolved] From 32accc8eab227aebcc40cc026897324119c0e589 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 22:56:10 +0900 Subject: [PATCH 113/198] Remove "osu!direct" button --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - osu.Game/Screens/Menu/Disclaimer.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 1270df5374..fa3f70735a 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -112,7 +112,7 @@ namespace osu.Game.Input.Bindings [Description("Toggle settings")] ToggleSettings, - [Description("Toggle osu!direct")] + [Description("Toggle beatmap listing")] ToggleDirect, [Description("Increase volume")] diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index fd4d74dc6b..0c47ef3b1e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -130,7 +130,6 @@ namespace osu.Game.Screens.Menu buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 46fddabb26..72eb9c7c0c 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -201,7 +201,7 @@ namespace osu.Game.Screens.Menu "New features are coming online every update. Make sure to stay up-to-date!", "If you find the UI too large or small, try adjusting UI scale in settings!", "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", - "For now, osu!direct is available to all users on lazer. You can access it anywhere using Ctrl-D!", + "For now, what used to be \"osu!direct\" is available to all users on lazer. You can access it anywhere using Ctrl-D!", "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", "Try scrolling down in the mod select panel to find a bunch of new fun mods!", From 59025e9d50a6866b71f2571919460c94a155222c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:09:58 +0900 Subject: [PATCH 114/198] Remove related events --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 - osu.Game/Screens/Menu/MainMenu.cs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 0c47ef3b1e..8417858878 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,7 +39,6 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; - public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMultiplayer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 97fd58318b..ef45a656fa 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) + private void load(SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -137,7 +137,6 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); - buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); From 283c69a68f3f4a1a29ea6d676fdaabdad67000aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:12:56 +0900 Subject: [PATCH 115/198] Update enum name in line with changes --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++-- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index fa3f70735a..b8c2fa201f 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), - new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), + new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), new KeyBinding(InputKey.Escape, GlobalAction.Back), @@ -113,7 +113,7 @@ namespace osu.Game.Input.Bindings ToggleSettings, [Description("Toggle beatmap listing")] - ToggleDirect, + ToggleBeatmapListing, [Description("Increase volume")] IncreaseVolume, diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index c495d673ce..bfe36a6a0f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarBeatmapListingButton() { - Hotkey = GlobalAction.ToggleDirect; + Hotkey = GlobalAction.ToggleBeatmapListing; } [BackgroundDependencyLoader(true)] From cf3043fc08fa723dde4f6c4a7ea4a060a4e72c50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:20:26 +0900 Subject: [PATCH 116/198] Only show "development build" footer on debug releases --- osu.Desktop/Overlays/VersionManager.cs | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 8c759f8487..59d0f9635d 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -26,9 +26,11 @@ namespace osu.Desktop.Overlays Alpha = 0; + FillFlowContainer mainFill; + Children = new Drawable[] { - new FillFlowContainer + mainFill = new FillFlowContainer { AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, @@ -55,23 +57,30 @@ namespace osu.Desktop.Overlays }, } }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.Numeric.With(size: 12), - Colour = colours.Yellow, - Text = @"Development Build" - }, - new Sprite - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Texture = textures.Get(@"Menu/dev-build-footer"), - }, } } }; + + if (DebugUtils.IsDebugBuild) + { + mainFill.AddRange(new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Numeric.With(size: 12), + Colour = colours.Yellow, + Text = @"Development Build" + }, + new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Texture = textures.Get(@"Menu/dev-build-footer"), + }, + }); + } } protected override void PopIn() From cfbfb8d58bd2768bf83be25c2e2da1cd26a1d4ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:21:46 +0900 Subject: [PATCH 117/198] Revert "Remove related events" This reverts commit 59025e9d50a6866b71f2571919460c94a155222c. --- osu.Game/Screens/Menu/ButtonSystem.cs | 1 + osu.Game/Screens/Menu/MainMenu.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8417858878..0c47ef3b1e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -39,6 +39,7 @@ namespace osu.Game.Screens.Menu public Action OnEdit; public Action OnExit; + public Action OnBeatmapListing; public Action OnSolo; public Action OnSettings; public Action OnMultiplayer; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index ef45a656fa..97fd58318b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; [BackgroundDependencyLoader(true)] - private void load(SettingsOverlay settings, OsuConfigManager config, SessionStatics statics) + private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable(Static.LoginOverlayDisplayed); @@ -137,6 +137,7 @@ namespace osu.Game.Screens.Menu }; buttons.OnSettings = () => settings?.ToggleVisibility(); + buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); From 35be7ec0e1477c9d904538c4dd726e50b6f2e827 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:28:14 +0900 Subject: [PATCH 118/198] Add back button but rename to "browse" --- osu.Game/Screens/Menu/ButtonSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 0c47ef3b1e..f400b2114b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -129,7 +129,8 @@ namespace osu.Game.Screens.Menu buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(@"edit", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(@"browse", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new Button(@"exit", string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); From d056e6575e1cd711890ffee0592b173b6eccb960 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Jan 2021 23:30:54 +0900 Subject: [PATCH 119/198] Use IsDeployedBuild instead of IsDebugBuild for footer display conditional --- osu.Desktop/Overlays/VersionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 59d0f9635d..e4a3451651 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -61,7 +61,7 @@ namespace osu.Desktop.Overlays } }; - if (DebugUtils.IsDebugBuild) + if (!game.IsDeployedBuild) { mainFill.AddRange(new Drawable[] { From a8530fde9d3dea6f4a0064e8371c2debbcdd2471 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 00:05:12 +0900 Subject: [PATCH 120/198] Tidy up variables and spacing --- osu.Desktop/DiscordRichPresence.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index 172db324cb..63b12fb84b 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -31,7 +31,8 @@ namespace osu.Desktop private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); - private readonly Bindable mode = new Bindable(); + + private readonly Bindable privacyMode = new Bindable(); private readonly RichPresence presence = new RichPresence { @@ -53,7 +54,8 @@ namespace osu.Desktop client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network); - config.BindWith(OsuSetting.DiscordRichPresence, mode); + config.BindWith(OsuSetting.DiscordRichPresence, privacyMode); + (user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u => { status.UnbindBindings(); @@ -66,7 +68,7 @@ namespace osu.Desktop ruleset.BindValueChanged(_ => updateStatus()); status.BindValueChanged(_ => updateStatus()); activity.BindValueChanged(_ => updateStatus()); - mode.BindValueChanged(_ => updateStatus()); + privacyMode.BindValueChanged(_ => updateStatus()); client.Initialize(); } @@ -82,7 +84,7 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline || mode.Value == DiscordRichPresenceMode.Off) + if (status.Value is UserStatusOffline || privacyMode.Value == DiscordRichPresenceMode.Off) { client.ClearPresence(); return; @@ -100,7 +102,7 @@ namespace osu.Desktop } // update user information - if (mode.Value == DiscordRichPresenceMode.Limited) + if (privacyMode.Value == DiscordRichPresenceMode.Limited) presence.Assets.LargeImageText = string.Empty; else presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty); @@ -144,7 +146,7 @@ namespace osu.Desktop return edit.Beatmap.ToString(); case UserActivity.InLobby lobby: - return mode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; + return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; } return string.Empty; From fb057857e707349ead9bf7aa539d5e61c3a42cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:40:29 +0100 Subject: [PATCH 121/198] Update references to current year --- Directory.Build.props | 4 ++-- LICENCE | 2 +- osu.Desktop/osu.nuspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9ec442aafa..049db816a8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ https://github.com/ppy/osu Automated release. ppy Pty Ltd - Copyright (c) 2020 ppy Pty Ltd + Copyright (c) 2021 ppy Pty Ltd osu game - \ No newline at end of file + diff --git a/LICENCE b/LICENCE index 2435c23545..b5962ad3b2 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2020 ppy Pty Ltd . +Copyright (c) 2021 ppy Pty Ltd . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec index 2fc6009183..fa182f8e70 100644 --- a/osu.Desktop/osu.nuspec +++ b/osu.Desktop/osu.nuspec @@ -11,7 +11,7 @@ false A free-to-win rhythm game. Rhythm is just a *click* away! testing - Copyright (c) 2020 ppy Pty Ltd + Copyright (c) 2021 ppy Pty Ltd en-AU From 09742998cdeaeaae7fda4c77309e45cd14b69b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:40:46 +0100 Subject: [PATCH 122/198] Fix mistaken obsoletion notice It was added in c9f38f7bb6af116fa7ec813ceb3f100dcad30adb, which specified 2021 in another place (and was committed in October of 2020 anyway). Update the year so that it doesn't get culled prematurely. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index da6da0ea97..e5eaf5db88 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -750,7 +750,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Result.Type != originalType) { Logger.Log($"{GetType().ReadableName()} applied an invalid hit result ({originalType}) when {nameof(HitResult.IgnoreMiss)} or {nameof(HitResult.IgnoreHit)} is expected.\n" - + $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2020-03-28 onwards.", level: LogLevel.Important); + + $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2021-03-28 onwards.", level: LogLevel.Important); } } From 539785e422accca0ccd360b4b02969ccf7a70a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:46:44 +0100 Subject: [PATCH 123/198] Remove obsoleted IHasCurve --- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 5 +- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 55 ------------------- 2 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasCurve.cs diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 36b421586e..df569b91c1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -10,10 +10,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset, -#pragma warning disable 618 - IHasCurve -#pragma warning restore 618 + internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset { /// /// Scoring distance with a speed-adjusted beat length of 1 second. diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs deleted file mode 100644 index 26f50ffa31..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osuTK; - -namespace osu.Game.Rulesets.Objects.Types -{ - [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public interface IHasCurve : IHasDistance, IHasRepeats - { - /// - /// The curve. - /// - SliderPath Path { get; } - } - -#pragma warning disable 618 - [Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126 - public static class HasCurveExtensions - { - /// - /// Computes the position on the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// The position on the curve. - public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) - => obj.Path.PositionAt(obj.ProgressAt(progress)); - - /// - /// Computes the progress along the curve relative to how much of the has been completed. - /// - /// The curve. - /// [0, 1] where 0 is the start time of the and 1 is the end time of the . - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - public static double ProgressAt(this IHasCurve obj, double progress) - { - double p = progress * obj.SpanCount() % 1; - if (obj.SpanAt(progress) % 2 == 1) - p = 1 - p; - return p; - } - - /// - /// Determines which span of the curve the progress point is on. - /// - /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// [0, SpanCount) where 0 is the first run. - public static int SpanAt(this IHasCurve obj, double progress) - => (int)(progress * obj.SpanCount()); - } -#pragma warning restore 618 -} From 9cc63e8dce8e65feba008ffb02195928f8ea8a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 17:48:46 +0100 Subject: [PATCH 124/198] Remove obsoleted IHasEndTime --- .../Rulesets/Objects/Types/IHasDuration.cs | 16 +++--------- .../Rulesets/Objects/Types/IHasEndTime.cs | 26 ------------------- 2 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Types/IHasEndTime.cs diff --git a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs index b558273650..ca734da5ad 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasDuration.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasDuration.cs @@ -6,26 +6,16 @@ namespace osu.Game.Rulesets.Objects.Types /// /// A HitObject that ends at a different time than its start time. /// -#pragma warning disable 618 - public interface IHasDuration : IHasEndTime -#pragma warning restore 618 + public interface IHasDuration { - double IHasEndTime.EndTime - { - get => EndTime; - set => Duration = (Duration - EndTime) + value; - } - - double IHasEndTime.Duration => Duration; - /// /// The time at which the HitObject ends. /// - new double EndTime { get; } + double EndTime { get; } /// /// The duration of the HitObject. /// - new double Duration { get; set; } + double Duration { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs b/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs deleted file mode 100644 index c3769c5909..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasEndTime.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using Newtonsoft.Json; - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that ends at a different time than its start time. - /// - [Obsolete("Use IHasDuration instead.")] // can be removed 20201126 - public interface IHasEndTime - { - /// - /// The time at which the HitObject ends. - /// - [JsonIgnore] - double EndTime { get; set; } - - /// - /// The duration of the HitObject. - /// - double Duration { get; } - } -} From 68352782db59f2db881ba5f443bf352cf8a9c03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 18:11:47 +0100 Subject: [PATCH 125/198] Change .StartsWith() to .Equals() In line with planned-but-delayed breaking change. --- osu.Game/Rulesets/RulesetStore.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index d422bca087..deabea57ef 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -100,9 +100,7 @@ namespace osu.Game.Rulesets foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { - // todo: StartsWith can be changed to Equals on 2020-11-08 - // This is to give users enough time to have their database use new abbreviated info). - if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.StartsWith(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) + if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) context.RulesetInfo.Add(r.RulesetInfo); } From 4998aaaa987b75de4d017f09067f44dd05caeec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 18:13:30 +0100 Subject: [PATCH 126/198] Remove outdated warning disable Does not trigger any more on Rider 2020.3.2. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 36f8fbedb3..7ba6e400bf 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -936,7 +936,6 @@ namespace osu.Game.Screens.Select Masking = false; } - // ReSharper disable once OptionalParameterHierarchyMismatch 2020.3 EAP4 bug. (https://youtrack.jetbrains.com/issue/RSRP-481535?p=RIDER-51910) protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) { UserScrolling = true; From 9984c80c87c428316d622523f805a4d7a4a4f196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 20:40:47 +0100 Subject: [PATCH 127/198] Make useless existing test actually fail --- .../Mods/TestSceneOsuModDifficultyAdjust.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs index 49c1fe8540..db8546c71b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModDifficultyAdjust.cs @@ -1,13 +1,17 @@ // 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 System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Tests.Mods @@ -18,8 +22,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestNoAdjustment() => CreateModTest(new ModTestData { Mod = new OsuModDifficultyAdjust(), + Beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 8 + } + }, + HitObjects = new List + { + new HitCircle { StartTime = 1000 }, + new HitCircle { StartTime = 2000 } + } + }, Autoplay = true, - PassCondition = checkSomeHit + PassCondition = () => checkSomeHit() && checkObjectsScale(0.29f) }); [Test] From 303cc62ee7c8eec32cb9cd4522202bb3cbdf512d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Jan 2021 21:57:54 +0100 Subject: [PATCH 128/198] Transfer flags indicating if settings were changed --- .../Mods/CatchModDifficultyAdjust.cs | 4 ++-- .../Mods/OsuModDifficultyAdjust.cs | 4 ++-- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index acdd0a420c..438d17dbc5 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Catch.Mods { base.ApplySettings(difficulty); - difficulty.CircleSize = CircleSize.Value; - difficulty.ApproachRate = ApproachRate.Value; + ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); + ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index ff995e38ce..a638234dbd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Osu.Mods { base.ApplySettings(difficulty); - difficulty.CircleSize = CircleSize.Value; - difficulty.ApproachRate = ApproachRate.Value; + ApplySetting(CircleSize, cs => difficulty.CircleSize = cs); + ApplySetting(ApproachRate, ar => difficulty.ApproachRate = ar); } } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 72a4bb297f..a531e885db 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -116,18 +116,30 @@ namespace osu.Game.Rulesets.Mods internal override void CopyAdjustedSetting(IBindable target, object source) { - userChangedSettings[target] = true; + // if the value is non-bindable, it's presumably coming from an external source (like the API) - therefore presume it is not default. + // if the value is bindable, defer to the source's IsDefault to be able to tell. + userChangedSettings[target] = !(source is IBindable bindableSource) || !bindableSource.IsDefault; base.CopyAdjustedSetting(target, source); } + /// + /// Applies a setting from a configuration bindable using , if it has been changed by the user. + /// + protected void ApplySetting(BindableNumber setting, Action applyFunc) + where T : struct, IComparable, IConvertible, IEquatable + { + if (userChangedSettings.TryGetValue(setting, out bool userChangedSetting) && userChangedSetting) + applyFunc.Invoke(setting.Value); + } + /// /// Apply all custom settings to the provided beatmap. /// /// The beatmap to have settings applied. protected virtual void ApplySettings(BeatmapDifficulty difficulty) { - difficulty.DrainRate = DrainRate.Value; - difficulty.OverallDifficulty = OverallDifficulty.Value; + ApplySetting(DrainRate, dr => difficulty.DrainRate = dr); + ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od); } } } From 6620eadec3f1c7fe5fac7b98d52683d1a05aba4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 18:47:03 +0900 Subject: [PATCH 129/198] Reduce default hover sound debounce interval --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 40899e7e95..22d59e70f7 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -24,7 +24,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Length of debounce for hover sound playback, in milliseconds. Default is 50ms. /// - public double HoverDebounceTime { get; } = 50; + public double HoverDebounceTime { get; } = 20; protected readonly HoverSampleSet SampleSet; From 8f52a83b297d277bf07bbda9e9e2efd2d0684092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 18:47:20 +0900 Subject: [PATCH 130/198] Share hover sound debounce across all instances via SessionStatics --- osu.Game/Configuration/SessionStatics.cs | 9 ++++++ .../Graphics/UserInterface/HoverSounds.cs | 29 +++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 03bc434aac..f28ee707b9 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; namespace osu.Game.Configuration { @@ -14,6 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); + Set(Static.LastHoverSoundPlaybackTime, 0.0); Set(Static.SeasonalBackgrounds, null); } } @@ -28,5 +31,11 @@ namespace osu.Game.Configuration /// Value under this lookup can be null if there are no backgrounds available (or API is not reachable). /// SeasonalBackgrounds, + + /// + /// The last playback time in milliseconds of a hover sample (from ). + /// Used to debounce hover sounds game-wide to avoid volume saturation, especially in scrolling views with many UI controls like . + /// + LastHoverSoundPlaybackTime } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 22d59e70f7..efd55c892c 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -5,11 +5,12 @@ using System.ComponentModel; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Threading; +using osu.Game.Configuration; namespace osu.Game.Graphics.UserInterface { @@ -28,31 +29,35 @@ namespace osu.Game.Graphics.UserInterface protected readonly HoverSampleSet SampleSet; + private Bindable lastPlaybackTime; + public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) { SampleSet = sampleSet; RelativeSizeAxes = Axes.Both; } - private ScheduledDelegate playDelegate; + [BackgroundDependencyLoader] + private void load(AudioManager audio, SessionStatics statics) + { + lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); + + sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } protected override bool OnHover(HoverEvent e) { - playDelegate?.Cancel(); + bool requiresDebounce = HoverDebounceTime <= 0; + bool enoughTimePassedSinceLastPlayback = lastPlaybackTime.Value == 0 || Time.Current - lastPlaybackTime.Value > HoverDebounceTime; - if (HoverDebounceTime <= 0) + if (!requiresDebounce || enoughTimePassedSinceLastPlayback) + { sampleHover?.Play(); - else - playDelegate = Scheduler.AddDelayed(() => sampleHover?.Play(), HoverDebounceTime); + lastPlaybackTime.Value = Time.Current; + } return base.OnHover(e); } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); - } } public enum HoverSampleSet From 69ac22dd7fdd477943ce041cb42072de318c15e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:06:10 +0900 Subject: [PATCH 131/198] Fix incorrectly copy pasted xmldoc --- osu.Game/Beatmaps/ControlPoints/ControlPoint.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs index 090675473d..e8dc623ddb 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPoint.cs @@ -30,8 +30,7 @@ namespace osu.Game.Beatmaps.ControlPoints public abstract bool IsRedundant(ControlPoint existing); /// - /// Create a copy of this room without online information. - /// Should be used to create a local copy of a room for submitting in the future. + /// Create an unbound copy of this control point. /// public ControlPoint CreateCopy() { From 00dc98e3ab0fef1b568969a5086d9ede48ec8003 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:06:52 +0900 Subject: [PATCH 132/198] Make legacy control point's BpmMultiplier setter private again --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index a06ad35b89..069a25b83d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,7 +164,7 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public float BpmMultiplier { get; set; } + public float BpmMultiplier { get; private set; } public LegacyDifficultyControlPoint(double beatLength) : this() From 42643fbaf69e321f8ae1d52e70ddbc17ac985a42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:10:19 +0900 Subject: [PATCH 133/198] Use already resolved EditorBeatmap rather than resolving a second time locally --- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index f180d7e63e..36fb0191b0 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -13,9 +13,6 @@ namespace osu.Game.Screens.Edit.Setup { internal class DifficultySection : SetupSection { - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - private LabelledSliderBar circleSizeSlider; private LabelledSliderBar healthDrainSlider; private LabelledSliderBar approachRateSlider; @@ -93,7 +90,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value; Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value; - editorBeatmap.UpdateAllHitObjects(); + Beatmap.UpdateAllHitObjects(); } } } From 77b55212a3c9870a0e42cd90e77e3ae4a552e273 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 19:11:51 +0900 Subject: [PATCH 134/198] Change access of beatmap to use working for consistency in file --- osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index b3bceceea3..010d7c2797 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox { Label = "Audio Track", - Current = { Value = Beatmap.Metadata.AudioFile ?? "Click to select a track" }, + Current = { Value = working.Value.Metadata.AudioFile ?? "Click to select a track" }, Target = audioTrackFileChooserContainer, TabbableContentContainer = this }, From 3c3e860dbc34d37855b79786a1abb754af1667e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Jan 2021 23:52:04 +0900 Subject: [PATCH 135/198] Move ControlPointInfo copying to base Beatmap.Clone method (and remove setter) --- osu.Game/Beatmaps/Beatmap.cs | 10 +++++++++- osu.Game/Beatmaps/IBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 -- osu.Game/Screens/Edit/EditorBeatmap.cs | 6 +----- osu.Game/Screens/Play/GameplayBeatmap.cs | 6 +----- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 5435e86dfd..be2006e67a 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -50,7 +50,15 @@ namespace osu.Game.Beatmaps IBeatmap IBeatmap.Clone() => Clone(); - public Beatmap Clone() => (Beatmap)MemberwiseClone(); + public Beatmap Clone() + { + var clone = (Beatmap)MemberwiseClone(); + + clone.ControlPointInfo = ControlPointInfo.CreateCopy(); + // todo: deep clone other elements as required. + + return clone; + } } public class Beatmap : Beatmap diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 7dd85e1232..8f27e0b0e9 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps /// /// The control points in this beatmap. /// - ControlPointInfo ControlPointInfo { get; set; } + ControlPointInfo ControlPointInfo { get; } /// /// The breaks in this beatmap. diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d25adca92b..30382c444f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -111,8 +111,6 @@ namespace osu.Game.Beatmaps // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); - converted.ControlPointInfo = converted.ControlPointInfo.CreateCopy(); - // Apply conversion mods to the result foreach (var mod in mods.OfType()) { diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index a54a95f59d..165d2ba278 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -74,11 +74,7 @@ namespace osu.Game.Screens.Edit public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo - { - get => PlayableBeatmap.ControlPointInfo; - set => PlayableBeatmap.ControlPointInfo = value; - } + public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; public List Breaks => PlayableBeatmap.Breaks; diff --git a/osu.Game/Screens/Play/GameplayBeatmap.cs b/osu.Game/Screens/Play/GameplayBeatmap.cs index 565595656f..64894544f4 100644 --- a/osu.Game/Screens/Play/GameplayBeatmap.cs +++ b/osu.Game/Screens/Play/GameplayBeatmap.cs @@ -29,11 +29,7 @@ namespace osu.Game.Screens.Play public BeatmapMetadata Metadata => PlayableBeatmap.Metadata; - public ControlPointInfo ControlPointInfo - { - get => PlayableBeatmap.ControlPointInfo; - set => PlayableBeatmap.ControlPointInfo = value; - } + public ControlPointInfo ControlPointInfo => PlayableBeatmap.ControlPointInfo; public List Breaks => PlayableBeatmap.Breaks; From 11801d61c1b3701356b253c9feb3a23eaa0d1dcf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:05:22 +0900 Subject: [PATCH 136/198] Use nullable doubule to better represent initial playback case --- osu.Game/Configuration/SessionStatics.cs | 2 +- osu.Game/Graphics/UserInterface/HoverSounds.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index f28ee707b9..382eab751b 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); - Set(Static.LastHoverSoundPlaybackTime, 0.0); + Set(Static.LastHoverSoundPlaybackTime, (double?)0.0); Set(Static.SeasonalBackgrounds, null); } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index efd55c892c..29b4bf5704 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -29,7 +29,7 @@ namespace osu.Game.Graphics.UserInterface protected readonly HoverSampleSet SampleSet; - private Bindable lastPlaybackTime; + private Bindable lastPlaybackTime; public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) { @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(AudioManager audio, SessionStatics statics) { - lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); + lastPlaybackTime = statics.GetBindable(Static.LastHoverSoundPlaybackTime); sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); } @@ -48,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { bool requiresDebounce = HoverDebounceTime <= 0; - bool enoughTimePassedSinceLastPlayback = lastPlaybackTime.Value == 0 || Time.Current - lastPlaybackTime.Value > HoverDebounceTime; + bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime; if (!requiresDebounce || enoughTimePassedSinceLastPlayback) { From e156bcdcae175f46a5c78fb9eb7c4ed5d0dbf5d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:05:34 +0900 Subject: [PATCH 137/198] Remove unnecessary (and broken) requiresDebounce check --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 29b4bf5704..4d30d61ff0 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -47,10 +47,9 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { - bool requiresDebounce = HoverDebounceTime <= 0; bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime; - if (!requiresDebounce || enoughTimePassedSinceLastPlayback) + if (enoughTimePassedSinceLastPlayback) { sampleHover?.Play(); lastPlaybackTime.Value = Time.Current; From c208800150494eceb06472e45310eecc8859b29b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 14:17:14 +0900 Subject: [PATCH 138/198] Fix auto selection scenario regressing due to scheduling too much --- osu.Game/Overlays/Mods/ModButton.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 51c8bcf9a9..b58e70cae6 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -52,9 +52,10 @@ namespace osu.Game.Overlays.Mods if (newIndex == selectedIndex) return false; int direction = newIndex < selectedIndex ? -1 : 1; + bool beforeSelected = Selected; - Mod modBefore = SelectedMod ?? Mods[0]; + Mod previousSelection = SelectedMod ?? Mods[0]; if (newIndex >= Mods.Length) newIndex = -1; @@ -64,25 +65,26 @@ namespace osu.Game.Overlays.Mods if (newIndex >= 0 && !Mods[newIndex].HasImplementation) return false; + selectedIndex = newIndex; + + Mod newSelection = SelectedMod ?? Mods[0]; + Schedule(() => { - selectedIndex = newIndex; - Mod modAfter = SelectedMod ?? Mods[0]; - if (beforeSelected != Selected) { iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); } - if (modBefore != modAfter) + if (previousSelection != newSelection) { const float rotate_angle = 16; foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.Mod = modAfter; + backgroundIcon.Mod = newSelection; using (BeginDelayedSequence(mod_switch_duration, true)) { @@ -94,15 +96,15 @@ namespace osu.Game.Overlays.Mods .RotateTo(rotate_angle * direction) .RotateTo(0f, mod_switch_duration, mod_switch_easing); - Schedule(() => displayMod(modAfter)); + Schedule(() => displayMod(newSelection)); } } foregroundIcon.Selected.Value = Selected; - - SelectionChanged?.Invoke(SelectedMod); }); + SelectionChanged?.Invoke(newSelection); + return true; } From a6766e64de95cdd23dfa17e029275be3e7ea3c15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 17:08:01 +0900 Subject: [PATCH 139/198] Add custom handling of Point serialization to fix startup crashes of tournament client SixLabors moved their data types around in a recent update (see https://github.com/ppy/osu-framework/pull/4025) and it was deemed that we should prefer `System.Drawing` primitives where possible. This was applied to the tournament client via https://github.com/ppy/osu/pull/11072 without correct consideration given to the fact that we serialize these types. `System.Drawing.Point` serializes into a comma separated string, which seems to be less correct than what we had, so I've switched back to the old format for the time being. We can reasses this in the future; the main goal here is to restore usability to the tournament client. Closes #11443. --- osu.Game.Tournament/JsonPointConverter.cs | 67 +++++++++++++++++++++++ osu.Game.Tournament/TournamentGameBase.cs | 7 ++- 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tournament/JsonPointConverter.cs diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs new file mode 100644 index 0000000000..57b91958d8 --- /dev/null +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Diagnostics; +using System.Drawing; +using Newtonsoft.Json; + +namespace osu.Game.Tournament +{ + /// + /// We made a change from using SixLabors.ImageSharp.Point to System.Drawing.Point at some stage. + /// This handles converting to a standardised format on json serialize/deserialize operations. + /// + internal class JsonPointConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, Point value, JsonSerializer serializer) + { + // use the format of LaborSharp's Point since it is nicer. + serializer.Serialize(writer, new SixLabors.ImageSharp.Point(value.X, value.Y)); + } + + public override Point ReadJson(JsonReader reader, Type objectType, Point existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.StartObject) + { + // if there's no object present then this is using string representation (System.Drawing.Point serializes to "x,y") + string str = (string)reader.Value; + + Debug.Assert(str != null); + + var split = str.Split(','); + + return new Point(int.Parse(split[0]), int.Parse(split[1])); + } + + var point = new Point(); + + while (reader.Read()) + { + if (reader.TokenType == JsonToken.EndObject) break; + + if (reader.TokenType == JsonToken.PropertyName) + { + var name = reader.Value?.ToString(); + int? val = reader.ReadAsInt32(); + + if (val == null) + continue; + + switch (name) + { + case "X": + point.X = val.Value; + break; + + case "Y": + point.Y = val.Value; + break; + } + } + } + + return point; + } + } +} diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index dbda6aa023..bc36f27e5b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -8,12 +8,12 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics.Textures; using osu.Framework.Input; -using osu.Framework.Platform; using osu.Framework.IO.Stores; +using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; -using osu.Game.Tournament.IPC; using osu.Game.Tournament.IO; +using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osu.Game.Users; using osuTK.Input; @@ -60,7 +60,7 @@ namespace osu.Game.Tournament { using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) using (var sr = new StreamReader(stream)) - ladder = JsonConvert.DeserializeObject(sr.ReadToEnd()); + ladder = JsonConvert.DeserializeObject(sr.ReadToEnd(), new JsonPointConverter()); } ladder ??= new LadderInfo(); @@ -251,6 +251,7 @@ namespace osu.Game.Tournament Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, + Converters = new JsonConverter[] { new JsonPointConverter() } })); } } From edd328c8fecdb2566c8f68f087f2c8089219bb77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Jan 2021 17:24:55 +0900 Subject: [PATCH 140/198] Move bindable closer to source class --- .../OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index c6018a79e9..04030cdbfd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -37,12 +37,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } + private IBindable operationInProgress; + private SampleChannel sampleReadyCount; private readonly ButtonWithTrianglesExposed button; private int countReady; - private IBindable operationInProgress; public MultiplayerReadyButton() { From 924f91ed9b91445d4cba0bd819441a0959021faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 15:56:35 +0100 Subject: [PATCH 141/198] Fix song select test doing the completely wrong thing --- .../Visual/SongSelect/TestSceneAdvancedStats.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 3d3517ada4..40b2f66d74 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Details; using osuTK.Graphics; @@ -141,16 +142,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { var ruleset = advancedStats.Beatmap.Ruleset.CreateInstance(); - var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); + var difficultyAdjustMod = ruleset.GetAllMods().OfType().Single(); var originalDifficulty = advancedStats.Beatmap.BaseDifficulty; - var adjustedDifficulty = new BeatmapDifficulty - { - CircleSize = originalDifficulty.CircleSize, - DrainRate = originalDifficulty.DrainRate - 0.5f, - OverallDifficulty = originalDifficulty.OverallDifficulty, - ApproachRate = originalDifficulty.ApproachRate + 2.2f, - }; - difficultyAdjustMod.ReadFromDifficulty(adjustedDifficulty); + + difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); + difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f; + difficultyAdjustMod.ApproachRate.Value = originalDifficulty.ApproachRate + 2.2f; SelectedMods.Value = new[] { difficultyAdjustMod }; }); From 9182f5dafb224baf6e6534edc7c727026231a1b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 00:38:38 +0900 Subject: [PATCH 142/198] Switch to using an anonymous type for serialisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tournament/JsonPointConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index 57b91958d8..7ad972f3e7 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tournament public override void WriteJson(JsonWriter writer, Point value, JsonSerializer serializer) { // use the format of LaborSharp's Point since it is nicer. - serializer.Serialize(writer, new SixLabors.ImageSharp.Point(value.X, value.Y)); + serializer.Serialize(writer, new { value.X, value.Y }); } public override Point ReadJson(JsonReader reader, Type objectType, Point existingValue, bool hasExistingValue, JsonSerializer serializer) From 82725b59c030d27abd3159ee3397a4b77e834b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 00:56:54 +0900 Subject: [PATCH 143/198] Use PointConverter --- osu.Game.Tournament/JsonPointConverter.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index 7ad972f3e7..9c82f8ac06 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -29,9 +29,7 @@ namespace osu.Game.Tournament Debug.Assert(str != null); - var split = str.Split(','); - - return new Point(int.Parse(split[0]), int.Parse(split[1])); + return new PointConverter().ConvertFromString(str) as Point? ?? new Point(); } var point = new Point(); From 0cf5be3ef432a718d23bab4fc951f04182375ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 17:02:57 +0100 Subject: [PATCH 144/198] Fix selection change event being invoked with wrong mod --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index b58e70cae6..ab8efdabcc 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Mods foregroundIcon.Selected.Value = Selected; }); - SelectionChanged?.Invoke(newSelection); + SelectionChanged?.Invoke(SelectedMod); return true; } From 8feaf3fb6ad084885f8b69b997caea7faf92bb50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 9 Jan 2021 01:24:18 +0900 Subject: [PATCH 145/198] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 611f0d05f4..492c88c7e4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6c220a5c21..f28a55e016 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5445adb3fb..93be3645ee 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From d507a08951b33618a00fcce8e3c61ab289ee9172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 18:16:03 +0100 Subject: [PATCH 146/198] Start with null last hover playback time --- osu.Game/Configuration/SessionStatics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 382eab751b..fd401119ff 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { Set(Static.LoginOverlayDisplayed, false); Set(Static.MutedAudioNotificationShownOnce, false); - Set(Static.LastHoverSoundPlaybackTime, (double?)0.0); + Set(Static.LastHoverSoundPlaybackTime, (double?)null); Set(Static.SeasonalBackgrounds, null); } } From 49c6abcb5c1a8eaa3baa8d0c3b94b2799b3e82cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 18:25:51 +0100 Subject: [PATCH 147/198] Remove mention of default value in xmldoc Just bound to get outdated with every change anyway. Look at the actual default value declaration to see what the default is. --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 4d30d61ff0..a1d06711db 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -23,7 +23,7 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleHover; /// - /// Length of debounce for hover sound playback, in milliseconds. Default is 50ms. + /// Length of debounce for hover sound playback, in milliseconds. /// public double HoverDebounceTime { get; } = 20; From 52789118a363bf0f648936d9d629d2afa554c76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 19:59:13 +0100 Subject: [PATCH 148/198] Schedule play button state update Revealed by the framework-side transform thread safety checks. `Stopped` is even annotated as not being thread-safe (but was annotated as such long after the class's nascence). --- osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index e95fdeecf4..04df27ce52 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -142,7 +142,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels AddInternal(preview); loading = false; - preview.Stopped += () => Playing.Value = false; + // make sure that the update of value of Playing (and the ensuing value change callbacks) + // are marshaled back to the update thread. + preview.Stopped += () => Schedule(() => Playing.Value = false); // user may have changed their mind. if (Playing.Value) From 274a045d8dbd03640280b422674bd5c7b0eebf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 20:01:47 +0100 Subject: [PATCH 149/198] Remove Dispose() override Culls another non-thread-safe mutation of the `Playing` bindable. It seems to be a weird vestige from an earlier revision of the old "direct" panel, which relied on `DisposeOnDeathRemoval` to finish track playback (and then was removed in 6c150c9ed793799fd6672cc2107c97c2e3844a09). The play button is no longer responsible for managing preview track lifetime anyway; `PreviewTrackManager`'s method are intended for that. --- osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index 04df27ce52..eb409785e0 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -163,11 +163,5 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (Preview?.Start() != true) Playing.Value = false; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - Playing.Value = false; - } } } From 284d30d336d0d7b851481a084c3067c45ff3cfdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 21:13:36 +0100 Subject: [PATCH 150/198] Move screen activity update to LoadComplete() Fixes a potential crash when moving from main menu to editor after having previously opened the login settings overlay. Setting the activity in BDL as done before is unsafe, as that set can trigger value change callbacks, which in turn can trigger adding transforms, which should always be done on the update thread. Semantically it also makes sense, as the user activity should change once the screen they're moving to has actually loaded and displayed to the user. --- osu.Game/Screens/OsuScreen.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index e1a29946f4..9b716b323d 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -143,7 +143,11 @@ namespace osu.Game.Screens private void load(OsuGame osu, AudioManager audio) { sampleExit = audio.Samples.Get(@"UI/screen-back"); + } + protected override void LoadComplete() + { + base.LoadComplete(); Activity.Value ??= InitialActivity; } From dad5dd36676de0c965c645b2564e496adb334908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:17:37 +0100 Subject: [PATCH 151/198] Remove unnecessary permissiveness wrt null --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 3 ++- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 39323d9db9..e539b315e4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,7 +241,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer void endOperation() { - readyClickOperation?.Dispose(); + Debug.Assert(readyClickOperation != null); + readyClickOperation.Dispose(); readyClickOperation = null; } } diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 6a340d7954..c34d39136e 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -40,13 +40,12 @@ namespace osu.Game.Screens.OnlinePlay return new InvokeOnDisposal(endOperation); } - /// - /// Ends tracking an online operation. - /// Does nothing if an operation has not been begun yet. - /// private void endOperation() { - leasedInProgress?.Return(); + if (leasedInProgress == null) + throw new InvalidOperationException("Cannot end operation multiple times."); + + leasedInProgress.Return(); leasedInProgress = null; } } From c2eeb822b84d007beec6190cda094ca3ea1ba062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:23:38 +0100 Subject: [PATCH 152/198] Rename {joiningRoom -> operationInProgress} --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 2a16a62714..9b4e78543c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby(); private readonly IBindable initialRoomsReceived = new Bindable(); - private readonly IBindable joiningRoom = new Bindable(); + private readonly IBindable operationInProgress = new Bindable(); private FilterControl filter; private Container content; @@ -110,8 +110,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge if (ongoingOperationTracker != null) { - joiningRoom.BindTo(ongoingOperationTracker.InProgress); - joiningRoom.BindValueChanged(_ => updateLoadingLayer(), true); + operationInProgress.BindTo(ongoingOperationTracker.InProgress); + operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true); } } @@ -187,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private void updateLoadingLayer() { - if (joiningRoom.Value || !initialRoomsReceived.Value) + if (operationInProgress.Value || !initialRoomsReceived.Value) loadingLayer.Show(); else loadingLayer.Hide(); From ff60d652ed6094054125990375dc9edc8ea05313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Jan 2021 22:28:21 +0100 Subject: [PATCH 153/198] Move out test ongoing operation tracker to higher level --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 6 +----- osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index a6037dcbf2..8b9bffcee1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets; -using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Tests.Resources; using osu.Game.Users; @@ -31,9 +30,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private BeatmapManager beatmaps; private RulesetStore rulesets; - [Cached] - private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private IDisposable readyClickOperation; [BackgroundDependencyLoader] @@ -66,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer }, OnReadyClick = async () => { - readyClickOperation = ongoingOperationTracker.BeginOperation(); + readyClickOperation = OngoingOperationTracker.BeginOperation(); if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready) { diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index da0e39d965..75ddb34685 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] public Bindable Filter { get; } + [Cached] + public OngoingOperationTracker OngoingOperationTracker { get; } = new OngoingOperationTracker(); + protected override Container Content => content; private readonly TestMultiplayerRoomContainer content; From 0aad0c7c6c5dd363bd5bb75d2eb4cc5c35a3804f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 9 Jan 2021 00:30:12 +0300 Subject: [PATCH 154/198] Target logic at `this` and adjust variables --- osu.Game/Rulesets/Mods/Mod.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7fe9a06597..e72e9a004f 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -144,12 +144,12 @@ namespace osu.Game.Rulesets.Mods foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = (IBindable)prop.GetValue(this); - var copyBindable = (IBindable)prop.GetValue(copy); + var targetBindable = (IBindable)prop.GetValue(this); + var sourceBindable = (IBindable)prop.GetValue(them); // we only care about changes that have been made away from defaults. - if (!origBindable.IsDefault) - copy.CopyAdjustedSetting(copyBindable, origBindable); + if (!sourceBindable.IsDefault) + CopyAdjustedSetting(targetBindable, sourceBindable); } } From 8c3955d34136026b2387864223b065c7f57b185e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 9 Jan 2021 21:38:20 +0100 Subject: [PATCH 155/198] Improve safety of ongoing operation tracker Finishing an operation started via `OngoingOperationTracker.BeginOperation()` was risky in cases where the operation ended at a callback on another thread (which, in the case of multiplayer, is *most* cases). In particular, if any consumer registered a callback that mutates transforms when the operation ends, it would result in crashes after the framework-side safety checks. Rework `OngoingOperationTracker` into an always-present component residing in the drawable hierarchy, and ensure that the `operationInProgress` bindable is always updated on the update thread. This way consumers don't have to add local schedules in multiple places. --- .../TestSceneCreateMultiplayerMatchButton.cs | 7 +------ .../Screens/OnlinePlay/OngoingOperationTracker.cs | 11 +++++++++-- osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs | 5 +++-- .../Tests/Visual/Multiplayer/MultiplayerTestScene.cs | 3 ++- .../Multiplayer/TestMultiplayerRoomContainer.cs | 4 ++++ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs index 381270c5aa..2f0398c6ef 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneCreateMultiplayerMatchButton.cs @@ -3,18 +3,13 @@ using System; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Multiplayer; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneCreateMultiplayerMatchButton : MultiplayerTestScene { - [Cached] - private OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); - private CreateMultiplayerMatchButton button; public override void SetUpSteps() @@ -36,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertButtonEnableState(true); - AddStep("begin joining room", () => joiningRoomOperation = ongoingOperationTracker.BeginOperation()); + AddStep("begin joining room", () => joiningRoomOperation = OngoingOperationTracker.BeginOperation()); assertButtonEnableState(false); AddStep("end joining room", () => joiningRoomOperation.Dispose()); diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index c34d39136e..5c9e9ce90b 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; namespace osu.Game.Screens.OnlinePlay { @@ -11,7 +12,7 @@ namespace osu.Game.Screens.OnlinePlay /// Utility class to track ongoing online operations' progress. /// Can be used to disable interactivity while waiting for a response from online sources. /// - public class OngoingOperationTracker + public class OngoingOperationTracker : Component { /// /// Whether there is an online operation in progress. @@ -22,6 +23,11 @@ namespace osu.Game.Screens.OnlinePlay private LeasedBindable leasedInProgress; + public OngoingOperationTracker() + { + AlwaysPresent = true; + } + /// /// Begins tracking a new online operation. /// @@ -37,7 +43,8 @@ namespace osu.Game.Screens.OnlinePlay leasedInProgress = inProgress.BeginLease(true); leasedInProgress.Value = true; - return new InvokeOnDisposal(endOperation); + // for extra safety, marshal the end of operation back to the update thread if necessary. + return new InvokeOnDisposal(() => Scheduler.Add(endOperation, false)); } private void endOperation() diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index b73e0a7c52..71fd0d5c76 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached] - private readonly OngoingOperationTracker ongoingOperationTracker = new OngoingOperationTracker(); + private OngoingOperationTracker ongoingOperationTracker { get; set; } [Resolved(CanBeNull = true)] private MusicController music { get; set; } @@ -144,7 +144,8 @@ namespace osu.Game.Screens.OnlinePlay }; button.Action = () => OpenNewRoom(); }), - RoomManager = CreateRoomManager() + RoomManager = CreateRoomManager(), + ongoingOperationTracker = new OngoingOperationTracker() } }; diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 75ddb34685..a87b22affe 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public Bindable Filter { get; } [Cached] - public OngoingOperationTracker OngoingOperationTracker { get; } = new OngoingOperationTracker(); + public OngoingOperationTracker OngoingOperationTracker { get; } protected override Container Content => content; private readonly TestMultiplayerRoomContainer content; @@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Client = content.Client; RoomManager = content.RoomManager; Filter = content.Filter; + OngoingOperationTracker = content.OngoingOperationTracker; } [SetUp] diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs index ad3e2f7105..860caef071 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomContainer.cs @@ -25,6 +25,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] public readonly Bindable Filter = new Bindable(new FilterCriteria()); + [Cached] + public readonly OngoingOperationTracker OngoingOperationTracker; + public TestMultiplayerRoomContainer() { RelativeSizeAxes = Axes.Both; @@ -33,6 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { Client = new TestMultiplayerClient(), RoomManager = new TestMultiplayerRoomManager(), + OngoingOperationTracker = new OngoingOperationTracker(), content = new Container { RelativeSizeAxes = Axes.Both } }); } From 4b4adc927cb49a166f9f13c093c318afbbe18f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 Jan 2021 15:35:53 +0100 Subject: [PATCH 156/198] Rename param to match method body --- osu.Game/Rulesets/Mods/Mod.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index e72e9a004f..3a8717e678 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -134,18 +134,18 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies mod setting values from into this instance. + /// Copies mod setting values from into this instance. /// - /// The mod to copy properties from. - public void CopyFrom(Mod them) + /// The mod to copy properties from. + public void CopyFrom(Mod source) { - if (them.GetType() != GetType()) - throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); + if (source.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source)); foreach (var (_, prop) in this.GetSettingsSourceProperties()) { var targetBindable = (IBindable)prop.GetValue(this); - var sourceBindable = (IBindable)prop.GetValue(them); + var sourceBindable = (IBindable)prop.GetValue(source); // we only care about changes that have been made away from defaults. if (!sourceBindable.IsDefault) From f466791b69d3341b05625157ae52301a4a035be6 Mon Sep 17 00:00:00 2001 From: Shivam Date: Sun, 10 Jan 2021 17:34:20 +0100 Subject: [PATCH 157/198] Move assignments to the TournamentSwitcher component This also adds conditional checks for displaying the "Close osu!" button --- osu.Game.Tournament/Screens/SetupScreen.cs | 41 ++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index de3321397e..7ae0375b9a 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -42,17 +42,13 @@ namespace osu.Game.Tournament.Screens [Resolved] private RulesetStore rulesets { get; set; } - [Resolved] - private TournamentGameBase game { get; set; } - [Resolved(canBeNull: true)] private TournamentSceneManager sceneManager { get; set; } private Bindable windowSize; - private TournamentStorage storage; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig, Storage storage) + private void load(FrameworkConfigManager frameworkConfig) { windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); @@ -67,7 +63,6 @@ namespace osu.Game.Tournament.Screens api.LocalUser.BindValueChanged(_ => Schedule(reload)); stableInfo.OnStableInfoSaved += () => Schedule(reload); - this.storage = (TournamentStorage)storage; reload(); } @@ -119,10 +114,6 @@ namespace osu.Game.Tournament.Screens { Label = "Current tournament", Description = "Changes the background videos and bracket to match the selected tournament. This requires a restart to apply changes.", - Items = storage.ListTournaments(), - Current = storage.CurrentTournament, - ButtonText = "Close osu!", - Action = () => game.GracefullyExit() }, resolution = new ResolutionSelector { @@ -240,14 +231,44 @@ namespace osu.Game.Tournament.Screens set => dropdown.Current = value; } + private string originalTournament; + + private TournamentStorage storage; + + [Resolved] + private TournamentGameBase game { get; set; } + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + this.storage = (TournamentStorage)storage; + Current = this.storage.CurrentTournament; + originalTournament = this.storage.CurrentTournament.Value; + Items = this.storage.ListTournaments(); + Action = () => game.GracefullyExit(); + ButtonText = "Close osu!"; + } + protected override Drawable CreateComponent() { var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, dropdown = new OsuDropdown { Width = 510 }); + Current.BindValueChanged(v => + { + if (v.NewValue == originalTournament) + { + Button.Hide(); + return; + } + + Button.Show(); + }); + return drawable; } } From bd377237884c071617df95c797650fd1af2774bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 01:47:04 +0900 Subject: [PATCH 158/198] Expose as IBindable for added safety --- .../BeatmapListing/Panels/BeatmapPanel.cs | 2 +- .../BeatmapListing/Panels/PlayButton.cs | 21 +++++++++++-------- .../BeatmapSet/Buttons/PreviewButton.cs | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 1567e18caa..afb5eeda36 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private Container content; public PreviewTrack Preview => PlayButton.Preview; - public Bindable PreviewPlaying => PlayButton?.Playing; + public IBindable PreviewPlaying => PlayButton?.Playing; protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index eb409785e0..4bbc3569fe 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -18,7 +18,10 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { public class PlayButton : Container { - public readonly BindableBool Playing = new BindableBool(); + public IBindable Playing => playing; + + private readonly BindableBool playing = new BindableBool(); + public PreviewTrack Preview { get; private set; } private BeatmapSetInfo beatmapSet; @@ -36,7 +39,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Preview?.Expire(); Preview = null; - Playing.Value = false; + playing.Value = false; } } @@ -82,7 +85,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); - Playing.ValueChanged += playingStateChanged; + playing.ValueChanged += playingStateChanged; } [Resolved] @@ -96,7 +99,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override bool OnClick(ClickEvent e) { - Playing.Toggle(); + playing.Toggle(); return true; } @@ -108,7 +111,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override void OnHoverLost(HoverLostEvent e) { - if (!Playing.Value) + if (!playing.Value) icon.FadeColour(Color4.White, 120, Easing.InOutQuint); base.OnHoverLost(e); } @@ -122,7 +125,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { if (BeatmapSet == null) { - Playing.Value = false; + playing.Value = false; return; } @@ -144,10 +147,10 @@ namespace osu.Game.Overlays.BeatmapListing.Panels loading = false; // make sure that the update of value of Playing (and the ensuing value change callbacks) // are marshaled back to the update thread. - preview.Stopped += () => Schedule(() => Playing.Value = false); + preview.Stopped += () => Schedule(() => playing.Value = false); // user may have changed their mind. - if (Playing.Value) + if (playing.Value) attemptStart(); }); } @@ -161,7 +164,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private void attemptStart() { if (Preview?.Start() != true) - Playing.Value = false; + playing.Value = false; } } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 6accce7d77..56d60a97b2 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -24,7 +24,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private readonly PlayButton playButton; private PreviewTrack preview => playButton.Preview; - public Bindable Playing => playButton.Playing; + + public IBindable Playing => playButton.Playing; public BeatmapSetInfo BeatmapSet { From d2ca6da0fdae2f14b7430aa0d183e31087f5c3f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 01:56:09 +0900 Subject: [PATCH 159/198] Remove unused constant --- osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 56d60a97b2..a5e5f664c9 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -18,8 +18,6 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { public class PreviewButton : OsuClickableContainer { - private const float transition_duration = 500; - private readonly Box background, progress; private readonly PlayButton playButton; From e99310b59cfddbad40882e187a79668cdd865bb4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:02:57 +0300 Subject: [PATCH 160/198] Add JsonConstructor attribute --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 1fd099fcc7..4796e26d14 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; namespace osu.Game.Online.Rooms { @@ -20,6 +21,7 @@ namespace osu.Game.Online.Rooms /// public readonly double? DownloadProgress; + [JsonConstructor] private BeatmapAvailability(DownloadState state, double? downloadProgress = null) { State = state; From a8dfa5e2a9b09e07893c83fb410b0c669e6868a5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:04:00 +0300 Subject: [PATCH 161/198] Rename typo'd method --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 4796e26d14..794e79ce55 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -28,7 +28,7 @@ namespace osu.Game.Online.Rooms DownloadProgress = downloadProgress; } - public static BeatmapAvailability NotDownload() => new BeatmapAvailability(DownloadState.NotDownloaded); + public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); From 2286e3679f75adb10b9ba7f32316db4e116cea71 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 08:21:07 +0300 Subject: [PATCH 162/198] Downloaded -> Importing --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 794e79ce55..b6b9c632fe 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.Rooms public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); - public static BeatmapAvailability Downloaded() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Downloaded); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; From 49057e8cbcca3921affcefca625365c5d9ccf23c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:35:42 +0900 Subject: [PATCH 163/198] Cache TournamentStorage explicitly for better safety --- osu.Game.Tournament/TournamentGameBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bc36f27e5b..97c950261b 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -40,6 +40,8 @@ namespace osu.Game.Tournament Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); dependencies.CacheAs(storage = new TournamentStorage(baseStorage)); + dependencies.CacheAs(storage); + dependencies.Cache(new TournamentVideoResourceStore(storage)); Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage))); From ba3a7a0501aad64940217bd104d4d74f96ed5e1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:35:47 +0900 Subject: [PATCH 164/198] Clean up code --- osu.Game.Tournament/Screens/SetupScreen.cs | 40 +++++----------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index 7ae0375b9a..cefad148a0 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -219,33 +219,22 @@ namespace osu.Game.Tournament.Screens { private OsuDropdown dropdown; - public IEnumerable Items - { - get => dropdown.Items; - set => dropdown.Items = value; - } - - public Bindable Current - { - get => dropdown.Current; - set => dropdown.Current = value; - } - - private string originalTournament; - - private TournamentStorage storage; + private string startupTournament; [Resolved] private TournamentGameBase game { get; set; } [BackgroundDependencyLoader] - private void load(Storage storage) + private void load(TournamentStorage storage) { - this.storage = (TournamentStorage)storage; - Current = this.storage.CurrentTournament; - originalTournament = this.storage.CurrentTournament.Value; - Items = this.storage.ListTournaments(); + dropdown.Current = storage.CurrentTournament; + dropdown.Items = storage.ListTournaments(); + dropdown.Current.BindValueChanged(v => Button.FadeTo(v.NewValue == startupTournament ? 0 : 1)); + + startupTournament = storage.CurrentTournament.Value; + Action = () => game.GracefullyExit(); + ButtonText = "Close osu!"; } @@ -258,17 +247,6 @@ namespace osu.Game.Tournament.Screens Width = 510 }); - Current.BindValueChanged(v => - { - if (v.NewValue == originalTournament) - { - Button.Hide(); - return; - } - - Button.Show(); - }); - return drawable; } } From bd627534b76edaf9bb71329eb50597ac3b17f9cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:38:51 +0900 Subject: [PATCH 165/198] Use disabled state instead of hiding button --- osu.Game.Tournament/Screens/SetupScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/SetupScreen.cs index cefad148a0..c7b299e07f 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/SetupScreen.cs @@ -227,11 +227,11 @@ namespace osu.Game.Tournament.Screens [BackgroundDependencyLoader] private void load(TournamentStorage storage) { + startupTournament = storage.CurrentTournament.Value; + dropdown.Current = storage.CurrentTournament; dropdown.Items = storage.ListTournaments(); - dropdown.Current.BindValueChanged(v => Button.FadeTo(v.NewValue == startupTournament ? 0 : 1)); - - startupTournament = storage.CurrentTournament.Value; + dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); Action = () => game.GracefullyExit(); From 7a7c583ded7de7e6a354f49d83f131b7471c89f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:44:07 +0900 Subject: [PATCH 166/198] Move setup screen classes out of single file and into their own namespace --- .../Screens/TestSceneSetupScreen.cs | 2 +- .../TestSceneStablePathSelectScreen.cs | 2 +- .../Screens/Setup/ActionableInfo.cs | 69 +++++++++ .../Screens/Setup/ResolutionSelector.cs | 50 ++++++ .../Screens/{ => Setup}/SetupScreen.cs | 145 +----------------- .../{ => Setup}/StablePathSelectScreen.cs | 4 +- .../Screens/Setup/TournamentSwitcher.cs | 43 ++++++ osu.Game.Tournament/TournamentSceneManager.cs | 1 + 8 files changed, 168 insertions(+), 148 deletions(-) create mode 100644 osu.Game.Tournament/Screens/Setup/ActionableInfo.cs create mode 100644 osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs rename osu.Game.Tournament/Screens/{ => Setup}/SetupScreen.cs (54%) rename osu.Game.Tournament/Screens/{ => Setup}/StablePathSelectScreen.cs (99%) create mode 100644 osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs index 650b4c5412..70b260c84c 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneSetupScreen.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Tournament.Screens; +using osu.Game.Tournament.Screens.Setup; namespace osu.Game.Tournament.Tests.Screens { diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs index 6e63b2d799..b422227788 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneStablePathSelectScreen.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.Game.Tournament.Screens; +using osu.Game.Tournament.Screens.Setup; namespace osu.Game.Tournament.Tests.Screens { diff --git a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs new file mode 100644 index 0000000000..f7d52a294e --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs @@ -0,0 +1,69 @@ +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class ActionableInfo : LabelledDrawable + { + protected OsuButton Button; + + public ActionableInfo() + : base(true) + { + } + + public string ButtonText + { + set => Button.Text = value; + } + + public string Value + { + set => valueText.Text = value; + } + + public bool Failing + { + set => valueText.Colour = value ? Color4.Red : Color4.White; + } + + public Action Action; + + private TournamentSpriteText valueText; + protected FillFlowContainer FlowContainer; + + protected override Drawable CreateComponent() => new Container + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Children = new Drawable[] + { + valueText = new TournamentSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + FlowContainer = new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + Button = new TriangleButton + { + Size = new Vector2(100, 40), + Action = () => Action?.Invoke() + } + } + } + } + }; + } +} diff --git a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs new file mode 100644 index 0000000000..47472c386c --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs @@ -0,0 +1,50 @@ +using System; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class ResolutionSelector : ActionableInfo + { + private const int minimum_window_height = 480; + private const int maximum_window_height = 2160; + + public new Action Action; + + private OsuNumberBox numberBox; + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + FlowContainer.Insert(-1, numberBox = new OsuNumberBox + { + Text = "1080", + Width = 100 + }); + + base.Action = () => + { + if (string.IsNullOrEmpty(numberBox.Text)) + return; + + // box contains text + if (!int.TryParse(numberBox.Text, out var number)) + { + // at this point, the only reason we can arrive here is if the input number was too big to parse into an int + // so clamp to max allowed value + number = maximum_window_height; + } + else + { + number = Math.Clamp(number, minimum_window_height, maximum_window_height); + } + + // in case number got clamped, reset number in numberBox + numberBox.Text = number.ToString(); + + Action?.Invoke(number); + }; + return drawable; + } + } +} diff --git a/osu.Game.Tournament/Screens/SetupScreen.cs b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs similarity index 54% rename from osu.Game.Tournament/Screens/SetupScreen.cs rename to osu.Game.Tournament/Screens/Setup/SetupScreen.cs index c7b299e07f..5d8f0405ca 100644 --- a/osu.Game.Tournament/Screens/SetupScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/SetupScreen.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Drawing; using osu.Framework.Allocation; @@ -9,19 +8,16 @@ using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Platform; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Tournament.IO; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osuTK; -using osuTK.Graphics; -namespace osu.Game.Tournament.Screens +namespace osu.Game.Tournament.Screens.Setup { public class SetupScreen : TournamentScreen, IProvideVideo { @@ -155,144 +151,5 @@ namespace osu.Game.Tournament.Screens Width = 0.5f, }; } - - private class ActionableInfo : LabelledDrawable - { - protected OsuButton Button; - - public ActionableInfo() - : base(true) - { - } - - public string ButtonText - { - set => Button.Text = value; - } - - public string Value - { - set => valueText.Text = value; - } - - public bool Failing - { - set => valueText.Colour = value ? Color4.Red : Color4.White; - } - - public Action Action; - - private TournamentSpriteText valueText; - protected FillFlowContainer FlowContainer; - - protected override Drawable CreateComponent() => new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new Drawable[] - { - valueText = new TournamentSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - FlowContainer = new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(10, 0), - Children = new Drawable[] - { - Button = new TriangleButton - { - Size = new Vector2(100, 40), - Action = () => Action?.Invoke() - } - } - } - } - }; - } - - private class TournamentSwitcher : ActionableInfo - { - private OsuDropdown dropdown; - - private string startupTournament; - - [Resolved] - private TournamentGameBase game { get; set; } - - [BackgroundDependencyLoader] - private void load(TournamentStorage storage) - { - startupTournament = storage.CurrentTournament.Value; - - dropdown.Current = storage.CurrentTournament; - dropdown.Items = storage.ListTournaments(); - dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); - - Action = () => game.GracefullyExit(); - - ButtonText = "Close osu!"; - } - - protected override Drawable CreateComponent() - { - var drawable = base.CreateComponent(); - - FlowContainer.Insert(-1, dropdown = new OsuDropdown - { - Width = 510 - }); - - return drawable; - } - } - - private class ResolutionSelector : ActionableInfo - { - private const int minimum_window_height = 480; - private const int maximum_window_height = 2160; - - public new Action Action; - - private OsuNumberBox numberBox; - - protected override Drawable CreateComponent() - { - var drawable = base.CreateComponent(); - FlowContainer.Insert(-1, numberBox = new OsuNumberBox - { - Text = "1080", - Width = 100 - }); - - base.Action = () => - { - if (string.IsNullOrEmpty(numberBox.Text)) - return; - - // box contains text - if (!int.TryParse(numberBox.Text, out var number)) - { - // at this point, the only reason we can arrive here is if the input number was too big to parse into an int - // so clamp to max allowed value - number = maximum_window_height; - } - else - { - number = Math.Clamp(number, minimum_window_height, maximum_window_height); - } - - // in case number got clamped, reset number in numberBox - numberBox.Text = number.ToString(); - - Action?.Invoke(number); - }; - return drawable; - } - } } } diff --git a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs similarity index 99% rename from osu.Game.Tournament/Screens/StablePathSelectScreen.cs rename to osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs index 717b43f704..03f79b644f 100644 --- a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs @@ -13,11 +13,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osu.Game.Tournament.IPC; using osu.Game.Tournament.Components; +using osu.Game.Tournament.IPC; using osuTK; -namespace osu.Game.Tournament.Screens +namespace osu.Game.Tournament.Screens.Setup { public class StablePathSelectScreen : TournamentScreen { diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs new file mode 100644 index 0000000000..a993a0594a --- /dev/null +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -0,0 +1,43 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Tournament.IO; + +namespace osu.Game.Tournament.Screens.Setup +{ + internal class TournamentSwitcher : ActionableInfo + { + private OsuDropdown dropdown; + + private string startupTournament; + + [Resolved] + private TournamentGameBase game { get; set; } + + [BackgroundDependencyLoader] + private void load(TournamentStorage storage) + { + startupTournament = storage.CurrentTournament.Value; + + dropdown.Current = storage.CurrentTournament; + dropdown.Items = storage.ListTournaments(); + dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true); + + Action = () => game.GracefullyExit(); + + ButtonText = "Close osu!"; + } + + protected override Drawable CreateComponent() + { + var drawable = base.CreateComponent(); + + FlowContainer.Insert(-1, dropdown = new OsuDropdown + { + Width = 510 + }); + + return drawable; + } + } +} \ No newline at end of file diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index 870ea466cc..ced1a8ec72 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -19,6 +19,7 @@ using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Ladder; using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.Schedule; +using osu.Game.Tournament.Screens.Setup; using osu.Game.Tournament.Screens.Showcase; using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamWin; From c9466426b743471e36e4126f3637d44c00fc819c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 14:45:01 +0900 Subject: [PATCH 167/198] Change field to local variable --- osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index a993a0594a..e38a374e1c 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -9,15 +9,13 @@ namespace osu.Game.Tournament.Screens.Setup { private OsuDropdown dropdown; - private string startupTournament; - [Resolved] private TournamentGameBase game { get; set; } [BackgroundDependencyLoader] private void load(TournamentStorage storage) { - startupTournament = storage.CurrentTournament.Value; + string startupTournament = storage.CurrentTournament.Value; dropdown.Current = storage.CurrentTournament; dropdown.Items = storage.ListTournaments(); @@ -40,4 +38,4 @@ namespace osu.Game.Tournament.Screens.Setup return drawable; } } -} \ No newline at end of file +} From f65042cf4452997589ba678b8c4670a16eafdd65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Jan 2021 15:47:27 +0900 Subject: [PATCH 168/198] Add missing licence headers --- osu.Game.Tournament/Screens/Setup/ActionableInfo.cs | 3 +++ osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs | 3 +++ osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs index f7d52a294e..cfdf9c99ae 100644 --- a/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs +++ b/osu.Game.Tournament/Screens/Setup/ActionableInfo.cs @@ -1,3 +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.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs index 47472c386c..4b518ea7c7 100644 --- a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs +++ b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs @@ -1,3 +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.Graphics; using osu.Game.Graphics.UserInterface; diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index e38a374e1c..74c872646c 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; From 90fb67b377e2022f3bfa60dfca4f31a7d3670228 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 11 Jan 2021 20:52:02 +0300 Subject: [PATCH 169/198] Update code in-line with decided direction --- osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs | 1 - osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 7fda526faf..f324a1a216 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -43,7 +43,6 @@ namespace osu.Game.Online.Multiplayer /// /// Change the user's local availability state of the beatmap set in joined room. - /// This will also force user state back to . /// /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index c155447f8c..7fbc770351 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -83,7 +83,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Debug.Assert(Room != null); ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged(userId, newBeatmapAvailability); - ChangeUserState(userId, MultiplayerUserState.Idle); } protected override Task JoinRoom(long roomId) From 0d5fbb15ac06c321a0f43d9a2e6ff5d9a61e8cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Jan 2021 20:28:24 +0100 Subject: [PATCH 170/198] Fix up code comments Default value restated in xmldoc was snipped because it's made redundant by the initialiser and possibly bound to be outdated at some point. --- osu.Game/Online/Multiplayer/IMultiplayerClient.cs | 2 +- osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 5410fbc030..19dd473230 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.Multiplayer Task UserStateChanged(int userId, MultiplayerUserState state); /// - /// Signals that a user in this room has their beatmap availability state changed. + /// Signals that a user in this room changed their beatmap availability state. /// /// The ID of the user whose beatmap availability state has changed. /// The new beatmap availability state of the user. diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index f324a1a216..09816974a7 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Online.Multiplayer Task ChangeState(MultiplayerUserState newState); /// - /// Change the user's local availability state of the beatmap set in joined room. + /// Change the local user's availability state of the current beatmap set in joined room. /// /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index f515b574df..2590acbc81 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -18,7 +18,7 @@ namespace osu.Game.Online.Multiplayer public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; /// - /// The availability state of the beatmap, set to by default. + /// The availability state of the current beatmap. /// public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 799b66020c..99aa8fe015 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -322,7 +322,7 @@ namespace osu.Game.Online.Multiplayer { var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); - // we don't care whether the room doesn't exist or user isn't in joined room, just return in that point. + // errors here are not critical - beatmap availability state is mostly for display. if (user == null) return; From 422260797b68cc6e3710d327c4e3accc66d4797c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Jan 2021 16:03:12 +0900 Subject: [PATCH 171/198] Revert polling changes to fix participant list display It turns out this polling was necessary to get extra data that isn't included in the main listing request. It was removed deemed useless, and in order to fix the order of rooms changing when selecting a room. Weirdly, I can't reproduce this happening any more, and on close inspection of the code can't see how it could happen in the first place. For now, let's revert this change and iterate from there, if/when the same issue arises again. I've discussed avoiding this second poll by potentially including more data (just `user_id`s?) in the main listing request, but not 100% sure on this - even if the returned data is minimal it's an extra join server-side, which could cause performance issues for large numbers of rooms. --- .../TestSceneMultiplayerRoomManager.cs | 1 + .../OnlinePlay/Multiplayer/Multiplayer.cs | 5 +++- .../Multiplayer/MultiplayerRoomManager.cs | 28 ++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 80d1acd145..7a3845cbf3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomManager = { TimeBetweenListingPolls = { Value = 1 }, + TimeBetweenSelectionPolls = { Value = 1 } } }; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 310617a0bc..76f5c74433 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -33,6 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!this.IsCurrentScreen()) { multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; } else { @@ -40,16 +41,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { case LoungeSubScreen _: multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; // Don't poll inside the match or anywhere else. default: multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } protected override Room CreateNewRoom() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 5c327266a3..3cb263298f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private StatefulMultiplayerClient multiplayerClient { get; set; } public readonly Bindable TimeBetweenListingPolls = new Bindable(); - + public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); private readonly IBindable isConnected = new Bindable(); private readonly Bindable allowPolling = new Bindable(); @@ -119,6 +119,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, AllowPolling = { BindTarget = allowPolling } }, + new MultiplayerSelectionPollingComponent + { + TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, + AllowPolling = { BindTarget = allowPolling } + } }; private class MultiplayerListingPollingComponent : ListingPollingComponent @@ -141,5 +146,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); } + + private class MultiplayerSelectionPollingComponent : SelectionPollingComponent + { + public readonly IBindable AllowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + AllowPolling.BindValueChanged(allowPolling => + { + if (!allowPolling.NewValue) + return; + + if (IsLoaded) + PollImmediately(); + }); + } + + protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + } } } From 22a0f99f35496d22b5b610626b057c36d1bea82d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jan 2021 17:49:18 +0900 Subject: [PATCH 172/198] Add failing test --- .../TaikoBeatmapConversionTest.cs | 1 + ...rating-drumroll-2-expected-conversion.json | 18 ++++++++++++++++++ .../Beatmaps/slider-generating-drumroll-2.osu | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 3d77fb05db..b6db333dc9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase("sample-to-type-conversions")] [TestCase("slider-conversion-v6")] [TestCase("slider-conversion-v14")] + [TestCase("slider-generating-drumroll-2")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json new file mode 100644 index 0000000000..b4ee98c86a --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2-expected-conversion.json @@ -0,0 +1,18 @@ +{ + "Mappings": [ + { + "StartTime": 51532, + "Objects": [ + { + "StartTime": 51532, + "EndTime": 52301, + "IsRim": false, + "IsCentre": false, + "IsDrumRoll": true, + "IsSwell": false, + "IsStrong": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu new file mode 100644 index 0000000000..d81b09ee26 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-generating-drumroll-2.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +Mode: 0 + +[Difficulty] +HPDrainRate:2 +CircleSize:3.2 +OverallDifficulty:2 +ApproachRate:3 +SliderMultiplier:0.999999999999999 +SliderTickRate:1 + +[TimingPoints] +763,384.615384615385,4,2,0,70,1,0 +49993,-90.9090909090909,4,2,0,75,0,1 + +[HitObjects] +51,245,51532,2,0,P|18:150|17:122,2,110.000003356934,0|8|0,0:0|0:0|0:0,0:0:0:0: From 9a22df2b88029de3f22b51938ad6b163322d95ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jan 2021 17:50:22 +0900 Subject: [PATCH 173/198] Fix BPM multiplier not working in all cases --- .../Beatmaps/TaikoBeatmapConverter.cs | 6 +- ...er-conversion-v14-expected-conversion.json | 307 +----------------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 5 +- 3 files changed, 18 insertions(+), 300 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 1214c594aa..b51f096d7d 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } } - private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out double taikoDuration, out double tickSpacing) + private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out int taikoDuration, out double tickSpacing) { // DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS. // Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable. @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; - taikoDuration = distance / taikoVelocity * beatLength; + taikoDuration = (int)(distance / taikoVelocity * beatLength); if (isForCurrentRuleset) { @@ -200,7 +200,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps beatLength = timingPoint.BeatLength; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); + tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, (double)taikoDuration / spans); return tickSpacing > 0 && distance / osuVelocity * 1000 < 2 * beatLength; diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json index 6a6063cb74..b7ad128cab 100644 --- a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/slider-conversion-v14-expected-conversion.json @@ -1,7 +1,9 @@ { - "Mappings": [{ + "Mappings": [ + { "StartTime": 2000, - "Objects": [{ + "Objects": [ + { "StartTime": 2000, "EndTime": 2000, "IsRim": false, @@ -23,7 +25,8 @@ }, { "StartTime": 4000, - "Objects": [{ + "Objects": [ + { "StartTime": 4000, "EndTime": 4000, "IsRim": false, @@ -45,7 +48,8 @@ }, { "StartTime": 6000, - "Objects": [{ + "Objects": [ + { "StartTime": 6000, "EndTime": 6000, "IsRim": true, @@ -76,300 +80,13 @@ }, { "StartTime": 8000, - "Objects": [{ + "Objects": [ + { "StartTime": 8000, - "EndTime": 8000, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8026, - "EndTime": 8026, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8053, - "EndTime": 8053, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8080, - "EndTime": 8080, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8107, - "EndTime": 8107, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8133, - "EndTime": 8133, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8160, - "EndTime": 8160, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8187, - "EndTime": 8187, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8214, - "EndTime": 8214, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8241, - "EndTime": 8241, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8267, - "EndTime": 8267, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8294, - "EndTime": 8294, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8321, - "EndTime": 8321, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8348, - "EndTime": 8348, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8374, - "EndTime": 8374, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8401, - "EndTime": 8401, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8428, - "EndTime": 8428, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8455, - "EndTime": 8455, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8482, - "EndTime": 8482, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8508, - "EndTime": 8508, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8535, - "EndTime": 8535, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8562, - "EndTime": 8562, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8589, - "EndTime": 8589, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8615, - "EndTime": 8615, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8642, - "EndTime": 8642, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8669, - "EndTime": 8669, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8696, - "EndTime": 8696, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8723, - "EndTime": 8723, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8749, - "EndTime": 8749, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8776, - "EndTime": 8776, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8803, - "EndTime": 8803, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8830, - "EndTime": 8830, - "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, - "IsSwell": false, - "IsStrong": false - }, - { - "StartTime": 8857, "EndTime": 8857, "IsRim": false, - "IsCentre": true, - "IsDrumRoll": false, + "IsCentre": false, + "IsDrumRoll": true, "IsSwell": false, "IsStrong": false } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 069a25b83d..2fb24c24e0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -164,12 +164,13 @@ namespace osu.Game.Beatmaps.Formats /// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it. /// DO NOT USE THIS UNLESS 100% SURE. /// - public float BpmMultiplier { get; private set; } + public double BpmMultiplier { get; private set; } public LegacyDifficultyControlPoint(double beatLength) : this() { - BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1; + // Note: In stable, the division occurs on floats, but with compiler optimisations turned on actually seems to occur on doubles via some .NET black magic (possibly inlining?). + BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100.0 : 1; } public LegacyDifficultyControlPoint() From b51b07c3a90c0dc1635984f9346175746c5e3da7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Jan 2021 18:05:29 +0900 Subject: [PATCH 174/198] Fix unstable multiplayer room ordering when selection is made --- .../OnlinePlay/Components/SelectionPollingComponent.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 0eec155060..dcf3c94b76 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -47,9 +48,11 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { - var rooms = new List(roomManager.Rooms); + // existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order. + var rooms = new List(roomManager.Rooms.OrderBy(r => r.Position.Value)); int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value); + if (index < 0) return; From 7298adc9d9b8ef878f4be5b8799be222aa0dd90b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Jan 2021 19:04:16 +0900 Subject: [PATCH 175/198] Fix non-threadsafe usage of MultiplayerClient.IsConnected --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 1 + .../OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs | 4 ++-- .../Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 4 ++-- .../OnlinePlay/Multiplayer/MultiplayerRoomManager.cs | 6 ++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 34cba09e8c..770039d79d 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -52,6 +52,7 @@ namespace osu.Game.Online.Multiplayer /// /// Whether the is currently connected. + /// This is NOT thread safe and usage should be scheduled. /// public abstract IBindable IsConnected { get; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs index 87b0e49b5b..a13d2cf540 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/CreateMultiplayerMatchButton.cs @@ -34,8 +34,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { base.LoadComplete(); - isConnected.BindValueChanged(_ => updateState()); - operationInProgress.BindValueChanged(_ => updateState(), true); + isConnected.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + operationInProgress.BindValueChanged(_ => Scheduler.AddOnce(updateState), true); } private void updateState() => Enabled.Value = isConnected.Value && !operationInProgress.Value; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 4bee502e2e..04d9e0a72a 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -77,14 +77,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }); isConnected = client.IsConnected.GetBoundCopy(); - isConnected.BindValueChanged(connected => + isConnected.BindValueChanged(connected => Schedule(() => { if (!connected.NewValue) { // messaging to the user about this disconnect will be provided by the MultiplayerMatchSubScreen. failAndBail(); } - }, true); + }), true); Debug.Assert(client.Room != null); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 5c327266a3..bcae2e5cbb 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -34,10 +34,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); isConnected.BindTo(multiplayerClient.IsConnected); - isConnected.BindValueChanged(_ => Schedule(updatePolling)); - JoinedRoom.BindValueChanged(_ => updatePolling()); - - updatePolling(); + isConnected.BindValueChanged(_ => Scheduler.AddOnce(updatePolling)); + JoinedRoom.BindValueChanged(_ => Scheduler.AddOnce(updatePolling), true); } public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) From 2d3cacca11bc1d8de6c78cf8b6f1154f1e058050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Jan 2021 00:58:53 +0100 Subject: [PATCH 176/198] Fix non-hosts crashing on load requested `onLoadRequested()` always released the `readyClickOperation` ongoing operation, without checking whether it actually needs to/should (it should only do so if the action initiating the operation was starting the game by the host). This would crash all other consumers, who already released the operation when their ready-up operation completed server side. To resolve, relax the constraint such that the operation can be ended multiple times in any order. At the end of the day the thing that matters is that the operation is done and the ready button is unblocked. --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index e539b315e4..80991569dc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,8 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer void endOperation() { - Debug.Assert(readyClickOperation != null); - readyClickOperation.Dispose(); + readyClickOperation?.Dispose(); readyClickOperation = null; } } @@ -255,9 +254,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); - Debug.Assert(readyClickOperation != null); - - readyClickOperation.Dispose(); + readyClickOperation?.Dispose(); readyClickOperation = null; } From 1f12b2bd091e7968c60e6c100cc9369edd5c3973 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 18:04:29 +0300 Subject: [PATCH 177/198] Rename download state `Downloaded` to `Importing` --- osu.Game/Graphics/UserInterface/DownloadButton.cs | 2 +- osu.Game/Online/DownloadState.cs | 2 +- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 2 +- .../Overlays/BeatmapListing/Panels/DownloadProgressBar.cs | 2 +- osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs | 2 +- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Screens/Ranking/ReplayDownloadButton.cs | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DownloadButton.cs b/osu.Game/Graphics/UserInterface/DownloadButton.cs index da6c95299e..5168ff646b 100644 --- a/osu.Game/Graphics/UserInterface/DownloadButton.cs +++ b/osu.Game/Graphics/UserInterface/DownloadButton.cs @@ -73,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface TooltipText = "Downloading..."; break; - case DownloadState.Downloaded: + case DownloadState.Importing: background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); TooltipText = "Importing"; break; diff --git a/osu.Game/Online/DownloadState.cs b/osu.Game/Online/DownloadState.cs index 72efbc286e..a58c40d16a 100644 --- a/osu.Game/Online/DownloadState.cs +++ b/osu.Game/Online/DownloadState.cs @@ -7,7 +7,7 @@ namespace osu.Game.Online { NotDownloaded, Downloading, - Downloaded, + Importing, LocallyAvailable } } diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index bed95344c6..7a64c9002d 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -106,7 +106,7 @@ namespace osu.Game.Online { if (attachedRequest.Progress == 1) { - State.Value = DownloadState.Downloaded; + State.Value = DownloadState.Importing; Progress.Value = 1; } else @@ -125,7 +125,7 @@ namespace osu.Game.Online } } - private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded); + private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Importing); private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 001ca801d9..cec1a5ac12 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels switch (State.Value) { case DownloadState.Downloading: - case DownloadState.Downloaded: + case DownloadState.Importing: shakeContainer.Shake(); break; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index 93cf8799b5..6a2f2e4569 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); break; - case DownloadState.Downloaded: + case DownloadState.Importing: progressBar.FadeIn(400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 56c0052bfe..cffff86a64 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -126,7 +126,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }; break; - case DownloadState.Downloaded: + case DownloadState.Importing: textSprites.Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 321e496511..c17901bb3f 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -287,7 +287,7 @@ namespace osu.Game.Overlays.BeatmapSet break; case DownloadState.Downloading: - case DownloadState.Downloaded: + case DownloadState.Importing: // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); break; diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index b76842f405..18b8649a59 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking scores.Download(Model.Value); break; - case DownloadState.Downloaded: + case DownloadState.Importing: case DownloadState.Downloading: shakeContainer.Shake(); break; From 1ba586a683b3321cf7806d9dabf068983d0bd27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Jan 2021 17:53:01 +0100 Subject: [PATCH 178/198] Revert overlooked AR<8 speed buff Pull request #11107 introduced changes in osu! performance calculation, related to a scaling coefficient applied to the speed and aim skills. The coefficient in question was dependent on the approach rate of a map. During a post-merge review of that PR, it was spotted that the scaling coefficient for speed also had a 10x buff applied for AR<8, which could reach magnitudes as large as 80% on AR0, which seems quite exorbitant. This change was not discussed or mentioned anywhere in the review process. Revert back to the old multiplier of 0.01 rather than 0.1 for AR<8. The negative slope through AR0 to 8 is retained in its previous form. --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index a44c97c3cf..44a9dd2f1f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (Attributes.ApproachRate > 10.33) approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33); else if (Attributes.ApproachRate < 8.0) - approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate); + approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate); aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0)); From 95acc457aad5f08df2b7a7a4bc37af4a241ffc2d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 22:34:48 +0300 Subject: [PATCH 179/198] Fix stupid mistake fuck. --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index b6b9c632fe..e7dbc5f436 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.Rooms public static BeatmapAvailability NotDownloaded() => new BeatmapAvailability(DownloadState.NotDownloaded); public static BeatmapAvailability Downloading(double progress) => new BeatmapAvailability(DownloadState.Downloading, progress); - public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Downloaded); + public static BeatmapAvailability Importing() => new BeatmapAvailability(DownloadState.Importing); public static BeatmapAvailability LocallyAvailable() => new BeatmapAvailability(DownloadState.LocallyAvailable); public bool Equals(BeatmapAvailability other) => other != null && State == other.State && DownloadProgress == other.DownloadProgress; From d5878db615ef55e87b9e39e22b480610f12cdf6c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Jan 2021 12:33:33 +0900 Subject: [PATCH 180/198] Fix default judgement text mispositioned for one frame --- osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index d94346cb72..21ac017685 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Judgements { JudgementText = new OsuSpriteText { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Text = Result.GetDescription().ToUpperInvariant(), Colour = colours.ForHitResult(Result), Font = OsuFont.Numeric.With(size: 20), From 8a0b975d71d08f6a845b10b46aec50d714d00c68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Jan 2021 18:25:32 +0900 Subject: [PATCH 181/198] Fix deadlock scenario when calculating fallback difficulty The previous code would run a calcaulation for the beatmap's own ruleset if the current one failed. While this does make sense, with the current way we use this component (and the implementation flow) it is quite unsafe. The to the call on `.Result` in the `catch` block, this would 100% deadlock due to the thread concurrency of the `ThreadedTaskScheduler` being 1. Even if the nested run could be run inline (it should be), the task scheduler won't even get to the point of checking whether this is feasible due to it being saturated by the already running task. I'm not sure if we still need this fallback lookup logic. After removing it, it's feasible that 0 stars will be returned during the scenario that previously caused a deadlock, but I don't necessarily think this is incorrect. There may be another reason for this needing to exist which I'm not aware of (diffcalc?) but if that's the case we may want to move the try-catch handling to the point of usage. To reproduce the deadlock scenario with 100% success (the repro instructions in the linked issue aren't that simple and require some patience and good timing), the main portion of the lookup can be changed to randomly trigger a nested lookup: ``` if (RNG.NextSingle() > 0.5f) return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result; else return new StarDifficulty(attributes); ``` After switching beatmap once or twice, pausing debug and viewing the state of threads should show exactly what is going on. --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 3b58062add..37d262abe5 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -260,17 +260,10 @@ namespace osu.Game.Beatmaps } catch (BeatmapInvalidForRulesetException e) { - // Conversion has failed for the given ruleset, so return the difficulty in the beatmap's default ruleset. - - // Ensure the beatmap's default ruleset isn't the one already being converted to. - // This shouldn't happen as it means something went seriously wrong, but if it does an endless loop should be avoided. if (rulesetInfo.Equals(beatmapInfo.Ruleset)) - { Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset})."); - return new StarDifficulty(); - } - return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result; + return new StarDifficulty(); } catch { From 4cccde9007cdb1646f041da71d77184c51f490d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 13:20:46 +0900 Subject: [PATCH 182/198] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 492c88c7e4..919d83f8db 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ff016199d3..ac014f2964 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -28,7 +28,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 93be3645ee..799042626b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 0a65ae8f1ef72f883e9f1e324bf3cd622bd321d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 14:07:24 +0900 Subject: [PATCH 183/198] Fix the beatmap carousel playing the difficulty change sample on beatmap change --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 40db04ae71..6c0bd3a228 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -500,7 +500,7 @@ namespace osu.Game.Screens.Select if (beatmap != null) { - if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) + if (beatmap.BeatmapSetInfoID == previous?.BeatmapInfo.BeatmapSetInfoID) sampleChangeDifficulty.Play(); else sampleChangeBeatmap.Play(); From ebbc32adfa9c4652cadff32dc5c429af9867ff09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 14:51:26 +0900 Subject: [PATCH 184/198] Change conditional used to decide legacy judgement animation to match stable In stable, the type of legacy judgement to show is based on the presence of particle textures in the skin. We were using the skin version instead, which turns out to be incorrect and not what some user skins expect. Closes #11078. --- osu.Game/Skinning/LegacySkin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index e4e5bf2f75..7397e3d08b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -378,8 +378,12 @@ namespace osu.Game.Skinning // kind of wasteful that we throw this away, but should do for now. if (createDrawable() != null) { - if (Configuration.LegacyVersion > 1) - return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, getParticleTexture(resultComponent.Component)); + var particle = getParticleTexture(resultComponent.Component); + + if (particle != null) + { + return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, particle); + } else return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable); } From 86f66727de47175b8045523c56f719a8051041a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 13:29:37 +0900 Subject: [PATCH 185/198] Update KeyBinding usages in line with interface changes --- osu.Game/Database/OsuDbContext.cs | 2 ++ osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs | 2 +- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- osu.Game/Input/KeyBindingStore.cs | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index 2ae07b3cf8..2aae62edea 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -135,6 +135,8 @@ namespace osu.Game.Database modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); modelBuilder.Entity().HasIndex(b => b.IntAction); + modelBuilder.Entity().Ignore(b => b.KeyCombination); + modelBuilder.Entity().Ignore(b => b.Action); modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index 94edc33099..d12eaa10f6 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings private KeyBindingStore store; - public override IEnumerable DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0); + public override IEnumerable DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0); /// /// Create a new instance. diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b8c2fa201f..8ccdb9249e 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -21,7 +21,7 @@ namespace osu.Game.Input.Bindings handler = game; } - public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings); + public override IEnumerable DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings).Concat(EditorKeyBindings); public IEnumerable GlobalKeyBindings => new[] { diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index bc73d74d74..b25b00eb84 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -49,7 +49,7 @@ namespace osu.Game.Input } } - private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) + private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { using (var usage = ContextFactory.GetForWrite()) { From 51255033e24c2afbbac85573ef5e7edf610eb920 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 13:41:35 +0900 Subject: [PATCH 186/198] Update some missed usages of KeyBindingContainer in tests --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index b2ad7ca5b4..802dbf2021 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestKeyBindingContainer : KeyBindingContainer { - public override IEnumerable DefaultKeyBindings => new[] + public override IEnumerable DefaultKeyBindings => new[] { new KeyBinding(InputKey.MouseLeft, TestAction.Down), }; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 40c4214749..6e338b7202 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestKeyBindingContainer : KeyBindingContainer { - public override IEnumerable DefaultKeyBindings => new[] + public override IEnumerable DefaultKeyBindings => new[] { new KeyBinding(InputKey.MouseLeft, TestAction.Down), }; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index b9ff95cb29..8278ff9adf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -303,7 +303,7 @@ namespace osu.Game.Tests.Visual.Gameplay internal class TestKeyBindingContainer : KeyBindingContainer { - public override IEnumerable DefaultKeyBindings => new[] + public override IEnumerable DefaultKeyBindings => new[] { new KeyBinding(InputKey.MouseLeft, TestAction.Down), }; From f42a6270bbd750e2539ddad3ff97c0adb29ee521 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 14:53:55 +0900 Subject: [PATCH 187/198] Update framework (again) for native libs fix --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 919d83f8db..db5c933c41 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ac014f2964..5e9e90c78f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -28,7 +28,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 799042626b..225cf981f2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From e0a4a666c8e5fa8224fae065d3597b49fa747103 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 15:01:16 +0900 Subject: [PATCH 188/198] Remove unnecessary workaround (mentioned package is pinned by SignalR to a working version) --- osu.Game/osu.Game.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5e9e90c78f..301ee39a61 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,8 +18,6 @@ - - From 7c612ec5561225b2a618bf11915aa693e20e7cf6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 15:11:03 +0900 Subject: [PATCH 189/198] Remove global.json --- global.json | 10 ---------- osu.sln | 1 - 2 files changed, 11 deletions(-) delete mode 100644 global.json diff --git a/global.json b/global.json deleted file mode 100644 index f5aaffcd3d..0000000000 --- a/global.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "sdk": { - "allowPrerelease": false, - "rollForward": "minor", - "version": "5.0.100" - }, - "msbuild-sdks": { - "Microsoft.Build.Traversal": "3.0.2" - } -} \ No newline at end of file diff --git a/osu.sln b/osu.sln index 1d64f6ff10..c9453359b1 100644 --- a/osu.sln +++ b/osu.sln @@ -57,7 +57,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig Directory.Build.props = Directory.Build.props - global.json = global.json osu.Android.props = osu.Android.props osu.iOS.props = osu.iOS.props CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset From 3f8834030416aeb80f5746a37999a83c16064d86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 15:17:38 +0900 Subject: [PATCH 190/198] Restore previous exception handling flow for stable path lookup --- osu.Desktop/OsuGameDesktop.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 55e42b160e..d1515acafa 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -59,10 +59,14 @@ namespace osu.Desktop if (OperatingSystem.IsWindows()) { - stableInstallPath = getStableInstallPathFromRegistry(); + try + { + stableInstallPath = getStableInstallPathFromRegistry(); - if (checkExists(stableInstallPath)) - return stableInstallPath; + if (!string.IsNullOrEmpty(stableInstallPath) && checkExists(stableInstallPath)) + return stableInstallPath; + } + catch { } } stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); From d023ad8ad1a03b500f13b90ca5d1d24a75e182ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 15:18:29 +0900 Subject: [PATCH 191/198] Remove assert messages --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 8278ff9adf..35b3bfc1f8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Gameplay switch (args.Action) { case NotifyCollectionChangedAction.Add: - Debug.Assert(args.NewItems != null, "args.NewItems != null"); + Debug.Assert(args.NewItems != null); foreach (int user in args.NewItems) { @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay break; case NotifyCollectionChangedAction.Remove: - Debug.Assert(args.OldItems != null, "args.OldItems != null"); + Debug.Assert(args.OldItems != null); foreach (int user in args.OldItems) { From b8c85ef017184b8672adb80401375bd3c8c8e224 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Jan 2021 16:03:12 +0900 Subject: [PATCH 192/198] Revert polling changes to fix participant list display It turns out this polling was necessary to get extra data that isn't included in the main listing request. It was removed deemed useless, and in order to fix the order of rooms changing when selecting a room. Weirdly, I can't reproduce this happening any more, and on close inspection of the code can't see how it could happen in the first place. For now, let's revert this change and iterate from there, if/when the same issue arises again. I've discussed avoiding this second poll by potentially including more data (just `user_id`s?) in the main listing request, but not 100% sure on this - even if the returned data is minimal it's an extra join server-side, which could cause performance issues for large numbers of rooms. --- .../TestSceneMultiplayerRoomManager.cs | 1 + .../OnlinePlay/Multiplayer/Multiplayer.cs | 5 +++- .../Multiplayer/MultiplayerRoomManager.cs | 28 ++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs index 80d1acd145..7a3845cbf3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerRoomManager.cs @@ -143,6 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RoomManager = { TimeBetweenListingPolls = { Value = 1 }, + TimeBetweenSelectionPolls = { Value = 1 } } }; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 310617a0bc..76f5c74433 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -33,6 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (!this.IsCurrentScreen()) { multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; } else { @@ -40,16 +41,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { case LoungeSubScreen _: multiplayerRoomManager.TimeBetweenListingPolls.Value = isIdle ? 120000 : 15000; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = isIdle ? 120000 : 15000; break; // Don't poll inside the match or anywhere else. default: multiplayerRoomManager.TimeBetweenListingPolls.Value = 0; + multiplayerRoomManager.TimeBetweenSelectionPolls.Value = 0; break; } } - Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value})"); + Logger.Log($"Polling adjusted (listing: {multiplayerRoomManager.TimeBetweenListingPolls.Value}, selection: {multiplayerRoomManager.TimeBetweenSelectionPolls.Value})"); } protected override Room CreateNewRoom() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 5c327266a3..3cb263298f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private StatefulMultiplayerClient multiplayerClient { get; set; } public readonly Bindable TimeBetweenListingPolls = new Bindable(); - + public readonly Bindable TimeBetweenSelectionPolls = new Bindable(); private readonly IBindable isConnected = new Bindable(); private readonly Bindable allowPolling = new Bindable(); @@ -119,6 +119,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer TimeBetweenPolls = { BindTarget = TimeBetweenListingPolls }, AllowPolling = { BindTarget = allowPolling } }, + new MultiplayerSelectionPollingComponent + { + TimeBetweenPolls = { BindTarget = TimeBetweenSelectionPolls }, + AllowPolling = { BindTarget = allowPolling } + } }; private class MultiplayerListingPollingComponent : ListingPollingComponent @@ -141,5 +146,26 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); } + + private class MultiplayerSelectionPollingComponent : SelectionPollingComponent + { + public readonly IBindable AllowPolling = new Bindable(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + AllowPolling.BindValueChanged(allowPolling => + { + if (!allowPolling.NewValue) + return; + + if (IsLoaded) + PollImmediately(); + }); + } + + protected override Task Poll() => !AllowPolling.Value ? Task.CompletedTask : base.Poll(); + } } } From ede5abdba4eb0f88806c38790c7a622546f70af9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Jan 2021 18:05:29 +0900 Subject: [PATCH 193/198] Fix unstable multiplayer room ordering when selection is made --- .../OnlinePlay/Components/SelectionPollingComponent.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs index 0eec155060..dcf3c94b76 100644 --- a/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs +++ b/osu.Game/Screens/OnlinePlay/Components/SelectionPollingComponent.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -47,9 +48,11 @@ namespace osu.Game.Screens.OnlinePlay.Components pollReq.Success += result => { - var rooms = new List(roomManager.Rooms); + // existing rooms need to be ordered by their position because the received of NotifyRoomsReceives expects to be able to sort them based on this order. + var rooms = new List(roomManager.Rooms.OrderBy(r => r.Position.Value)); int index = rooms.FindIndex(r => r.RoomID.Value == result.RoomID.Value); + if (index < 0) return; From 2b578e97e532fc0bb215566b8bd34df25d1d4625 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Jan 2021 18:25:32 +0900 Subject: [PATCH 194/198] Fix deadlock scenario when calculating fallback difficulty The previous code would run a calcaulation for the beatmap's own ruleset if the current one failed. While this does make sense, with the current way we use this component (and the implementation flow) it is quite unsafe. The to the call on `.Result` in the `catch` block, this would 100% deadlock due to the thread concurrency of the `ThreadedTaskScheduler` being 1. Even if the nested run could be run inline (it should be), the task scheduler won't even get to the point of checking whether this is feasible due to it being saturated by the already running task. I'm not sure if we still need this fallback lookup logic. After removing it, it's feasible that 0 stars will be returned during the scenario that previously caused a deadlock, but I don't necessarily think this is incorrect. There may be another reason for this needing to exist which I'm not aware of (diffcalc?) but if that's the case we may want to move the try-catch handling to the point of usage. To reproduce the deadlock scenario with 100% success (the repro instructions in the linked issue aren't that simple and require some patience and good timing), the main portion of the lookup can be changed to randomly trigger a nested lookup: ``` if (RNG.NextSingle() > 0.5f) return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result; else return new StarDifficulty(attributes); ``` After switching beatmap once or twice, pausing debug and viewing the state of threads should show exactly what is going on. --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 3b58062add..37d262abe5 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -260,17 +260,10 @@ namespace osu.Game.Beatmaps } catch (BeatmapInvalidForRulesetException e) { - // Conversion has failed for the given ruleset, so return the difficulty in the beatmap's default ruleset. - - // Ensure the beatmap's default ruleset isn't the one already being converted to. - // This shouldn't happen as it means something went seriously wrong, but if it does an endless loop should be avoided. if (rulesetInfo.Equals(beatmapInfo.Ruleset)) - { Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset})."); - return new StarDifficulty(); - } - return GetAsync(new DifficultyCacheLookup(key.Beatmap, key.Beatmap.Ruleset, key.OrderedMods)).Result; + return new StarDifficulty(); } catch { From ed78be825f9a988df3920fdbda730120bebfe125 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 15:47:41 +0900 Subject: [PATCH 195/198] Fix editor timeline not snapping on non-precise wheel scroll For wheel input with precision, we still prefer exact tracking for now. May change this in the future based on feedback from mappers, but it makes little sense to do non-snapped scrolling when input is coming from a non-precise source. --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 20836c0e68..12f7625bf9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -138,6 +138,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline scrollToTrackTime(); } + protected override bool OnScroll(ScrollEvent e) + { + // if this is not a precision scroll event, let the editor handle the seek itself (for snapping support) + if (!e.AltPressed && !e.IsPrecise) + return false; + + return base.OnScroll(e); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); From 88a27124c095f5a3cff7eaddaaab92cfe1832d55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:13:27 +0900 Subject: [PATCH 196/198] Make long spinner test longer and fix step name --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 496b1b3559..c22b1dc407 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestLongSpinner(bool autoplay) { - AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 2000))); + AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000))); AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0)); } From 6adb6b6700b4b19336955b397d5939679919d70b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:13:52 +0900 Subject: [PATCH 197/198] Fix spinner tests not playing spinning sound due to empty hitsamples --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index c22b1dc407..f697a77d94 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mods; @@ -55,7 +57,11 @@ namespace osu.Game.Rulesets.Osu.Tests var spinner = new Spinner { StartTime = Time.Current + delay, - EndTime = Time.Current + delay + length + EndTime = Time.Current + delay + length, + Samples = new List + { + new HitSampleInfo("hitnormal") + } }; spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); From 3c1a86d11deefc694a00ebad3369c563a07168de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 15 Jan 2021 22:04:45 +0100 Subject: [PATCH 198/198] Trim braces for consistency --- osu.Game/Skinning/LegacySkin.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7397e3d08b..090ffaebd7 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -381,9 +381,7 @@ namespace osu.Game.Skinning var particle = getParticleTexture(resultComponent.Component); if (particle != null) - { return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, particle); - } else return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable); }