From 3b600f0a7b2dda8fa17ba85ee1563526bad44ece Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 20 Nov 2020 10:45:19 +0300 Subject: [PATCH 001/385] 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/385] 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/385] 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/385] 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/385] 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/385] 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/385] 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 5d7294451f1a5a24a7eabdf7a738e6fc07278b17 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 16 Dec 2020 14:28:16 +0100 Subject: [PATCH 008/385] Refactor Import() overload to take a list of import tasks instead. --- osu.Android/OsuGameActivity.cs | 3 ++- osu.Game/Database/ArchiveModelManager.cs | 4 ++-- osu.Game/Database/ICanAcceptFiles.cs | 7 ++----- osu.Game/OsuGame.cs | 6 +++--- osu.Game/OsuGameBase.cs | 6 +++--- osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 +- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index e801c2ca6e..bd5523f0e2 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -11,6 +11,7 @@ using Android.OS; using Android.Provider; using Android.Views; using osu.Framework.Android; +using osu.Game.Database; namespace osu.Android { @@ -82,7 +83,7 @@ namespace osu.Android using (var stream = ContentResolver.OpenInputStream(uri)) await stream.CopyToAsync(copy); - await game.Import(copy, filename); + await game.Import(new ImportTask(copy, filename)); }, TaskCreationOptions.LongRunning); } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 36cc4cce39..9f69ad035f 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -115,13 +115,13 @@ namespace osu.Game.Database return Import(notification, paths.Select(p => new ImportTask(p)).ToArray()); } - public Task Import(Stream stream, string filename) + public Task Import(params ImportTask[] tasks) { var notification = new ProgressNotification { State = ProgressNotificationState.Active }; PostNotification?.Invoke(notification); - return Import(notification, new ImportTask(stream, filename)); + return Import(notification, tasks); } protected async Task> Import(ProgressNotification notification, params ImportTask[] tasks) diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs index 276c284c9f..5ec187975a 100644 --- a/osu.Game/Database/ICanAcceptFiles.cs +++ b/osu.Game/Database/ICanAcceptFiles.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; namespace osu.Game.Database @@ -19,11 +18,9 @@ namespace osu.Game.Database Task Import(params string[] paths); /// - /// Import the provided stream as a simple item. + /// Import the specified files from the given import tasks. /// - /// The stream to import files from. Should be in a supported archive format. - /// The filename of the archive being imported. - Task Import(Stream stream, string filename); + Task Import(params ImportTask[] tasks); /// /// An array of accepted file extensions (in the standard format of ".abc"). diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d67d790ce2..1f5b991758 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -52,7 +52,7 @@ using osu.Game.Updater; using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; using osu.Game.Users; -using System.IO; +using osu.Game.Database; namespace osu.Game { @@ -427,10 +427,10 @@ namespace osu.Game }, validScreens: new[] { typeof(PlaySongSelect) }); } - public override Task Import(Stream stream, string filename) + public override Task Import(params ImportTask[] imports) { // encapsulate task as we don't want to begin the import process until in a ready state. - var importTask = new Task(async () => await base.Import(stream, filename)); + var importTask = new Task(async () => await base.Import(imports)); waitForReady(() => this, _ => importTask.Start()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 150569f1dd..0a579cc347 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -395,14 +395,14 @@ namespace osu.Game } } - public virtual async Task Import(Stream stream, string filename) + public virtual async Task Import(params ImportTask[] tasks) { - var extension = Path.GetExtension(filename)?.ToLowerInvariant(); + var extension = Path.GetExtension(tasks.First().Path)?.ToLowerInvariant(); foreach (var importer in fileImporters) { if (importer.HandledExtensions.Contains(extension)) - await importer.Import(stream, Path.GetFileNameWithoutExtension(filename)); + await importer.Import(tasks); } } diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 0c957b80af..fe9b10667a 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Setup return Task.CompletedTask; } - Task ICanAcceptFiles.Import(Stream stream, string filename) => throw new NotImplementedException(); + Task ICanAcceptFiles.Import(params ImportTask[] tasks) => throw new NotImplementedException(); protected override void LoadComplete() { From 9d8906924580cc3886759de27dbbb05fb5460b00 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 16 Dec 2020 20:33:29 +0100 Subject: [PATCH 009/385] Add ability to import multiple files at once on android. --- osu.Android/OsuGameActivity.cs | 63 ++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index bd5523f0e2..bf73f33b74 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.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 System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Android.App; using Android.Content; @@ -16,7 +18,7 @@ using osu.Game.Database; namespace osu.Android { [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] - [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] + [IntentFilter(new[] { Intent.ActionDefault, Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataPathPatterns = new[] { ".*\\.osz", ".*\\.osk" }, DataMimeType = "application/*")] public class OsuGameActivity : AndroidGameActivity { private OsuGameAndroid game; @@ -49,41 +51,64 @@ namespace osu.Android { case Intent.ActionDefault: if (intent.Scheme == ContentResolver.SchemeContent) - handleImportFromUri(intent.Data); + handleImportFromUris(intent.Data); break; case Intent.ActionSend: { var content = intent.ClipData?.GetItemAt(0); if (content != null) - handleImportFromUri(content.Uri); + handleImportFromUris(content.Uri); + break; + } + + case Intent.ActionSendMultiple: + { + var uris = new List(); + for (int i = 0; i < intent.ClipData?.ItemCount; i++) + { + var content = intent.ClipData?.GetItemAt(i); + if (content != null) + uris.Add(content.Uri); + } + handleImportFromUris(uris.ToArray()); break; } } } - private void handleImportFromUri(Uri uri) => Task.Factory.StartNew(async () => + private void handleImportFromUris(params Uri[] uris) => Task.Factory.StartNew(async () => { - // there are more performant overloads of this method, but this one is the most backwards-compatible - // (dates back to API 1). - var cursor = ContentResolver?.Query(uri, null, null, null, null); + var tasks = new List(); - if (cursor == null) - return; + await Task.WhenAll(uris.Select(async uri => + { + // there are more performant overloads of this method, but this one is the most backwards-compatible + // (dates back to API 1). + var cursor = ContentResolver?.Query(uri, null, null, null, null); - cursor.MoveToFirst(); + if (cursor == null) + return; - var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); - string filename = cursor.GetString(filenameColumn); + cursor.MoveToFirst(); - // SharpCompress requires archive streams to be seekable, which the stream opened by - // OpenInputStream() seems to not necessarily be. - // copy to an arbitrary-access memory stream to be able to proceed with the import. - var copy = new MemoryStream(); - using (var stream = ContentResolver.OpenInputStream(uri)) - await stream.CopyToAsync(copy); + var filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); + string filename = cursor.GetString(filenameColumn); - await game.Import(new ImportTask(copy, filename)); + // SharpCompress requires archive streams to be seekable, which the stream opened by + // OpenInputStream() seems to not necessarily be. + // copy to an arbitrary-access memory stream to be able to proceed with the import. + var copy = new MemoryStream(); + using (var stream = ContentResolver.OpenInputStream(uri)) + await stream.CopyToAsync(copy); + + lock (tasks) + { + tasks.Add(new ImportTask(copy, filename)); + } + })); + + await game.Import(tasks.ToArray()); }, TaskCreationOptions.LongRunning); } } From cc0442a9a17880bd6f427638f831f97f6f50cb50 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 16 Dec 2020 20:42:30 +0100 Subject: [PATCH 010/385] Fix CI inspections. --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0a579cc347..521778a9cd 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -397,7 +397,7 @@ namespace osu.Game public virtual async Task Import(params ImportTask[] tasks) { - var extension = Path.GetExtension(tasks.First().Path)?.ToLowerInvariant(); + var extension = Path.GetExtension(tasks.First().Path).ToLowerInvariant(); foreach (var importer in fileImporters) { From 926281831b85353c1ddf878167abfbbad965a56f Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 19 Dec 2020 10:36:27 +0100 Subject: [PATCH 011/385] Fix missing XMLDoc bit. --- osu.Game/Database/ICanAcceptFiles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Database/ICanAcceptFiles.cs b/osu.Game/Database/ICanAcceptFiles.cs index 5ec187975a..74fd6fcc36 100644 --- a/osu.Game/Database/ICanAcceptFiles.cs +++ b/osu.Game/Database/ICanAcceptFiles.cs @@ -20,6 +20,7 @@ namespace osu.Game.Database /// /// Import the specified files from the given import tasks. /// + /// The import tasks from which the files should be imported. Task Import(params ImportTask[] tasks); /// From 4fba0c8e6a3279216294a5a75fa2f79687d0a0c1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 19 Dec 2020 10:55:39 +0100 Subject: [PATCH 012/385] Remove not used using statement. --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4ebf7fbe63..e382ff5d48 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -51,7 +51,6 @@ using osu.Game.Screens.Select; using osu.Game.Updater; using osu.Game.Utils; using LogLevel = osu.Framework.Logging.LogLevel; -using osu.Game.Users; using osu.Game.Database; namespace osu.Game From f1aefcdf86a9ef784644614915bd11c1476c9816 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 19 Dec 2020 22:48:39 +0100 Subject: [PATCH 013/385] Handle multiple extensions in the import files. --- osu.Game/OsuGameBase.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 521778a9cd..ca87772209 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -397,13 +397,18 @@ namespace osu.Game public virtual async Task Import(params ImportTask[] tasks) { - var extension = Path.GetExtension(tasks.First().Path).ToLowerInvariant(); + var importTasks = new List(); - foreach (var importer in fileImporters) + foreach (var extension in tasks.Select(t => Path.GetExtension(t.Path)).Distinct()) { - if (importer.HandledExtensions.Contains(extension)) - await importer.Import(tasks); + var importList = tasks.Where(t => t.Path.EndsWith(extension, StringComparison.OrdinalIgnoreCase)); + var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(extension)); + + if (importer != null) + importTasks.Add(importer.Import(importList.ToArray())); } + + await Task.WhenAll(importTasks); } public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); From 668f89d8b230103d8daee7fd639fde069baa4d99 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:33:11 +0200 Subject: [PATCH 014/385] Copy test from #11019 --- .../TestSceneSectionsContainer.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs new file mode 100644 index 0000000000..5c2e6e457d --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSectionsContainer.cs @@ -0,0 +1,122 @@ +// 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.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneSectionsContainer : OsuManualInputManagerTestScene + { + private readonly SectionsContainer container; + private float custom; + private const float header_height = 100; + + public TestSceneSectionsContainer() + { + container = new SectionsContainer + { + RelativeSizeAxes = Axes.Y, + Width = 300, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + FixedHeader = new Box + { + Alpha = 0.5f, + Width = 300, + Height = header_height, + Colour = Color4.Red + } + }; + container.SelectedSection.ValueChanged += section => + { + if (section.OldValue != null) + section.OldValue.Selected = false; + if (section.NewValue != null) + section.NewValue.Selected = true; + }; + Add(container); + } + + [Test] + public void TestSelection() + { + AddStep("clear", () => container.Clear()); + AddStep("add 1/8th", () => append(1 / 8.0f)); + AddStep("add third", () => append(1 / 3.0f)); + AddStep("add half", () => append(1 / 2.0f)); + AddStep("add full", () => append(1)); + AddSliderStep("set custom", 0.1f, 1.1f, 0.5f, i => custom = i); + AddStep("add custom", () => append(custom)); + AddStep("scroll to previous", () => container.ScrollTo( + container.Children.Reverse().SkipWhile(s => s != container.SelectedSection.Value).Skip(1).FirstOrDefault() ?? container.Children.First() + )); + AddStep("scroll to next", () => container.ScrollTo( + container.Children.SkipWhile(s => s != container.SelectedSection.Value).Skip(1).FirstOrDefault() ?? container.Children.Last() + )); + AddStep("scroll up", () => triggerUserScroll(1)); + AddStep("scroll down", () => triggerUserScroll(-1)); + } + + [Test] + public void TestCorrectSectionSelected() + { + const int sections_count = 11; + float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f }; + AddStep("clear", () => container.Clear()); + AddStep("fill with sections", () => + { + for (int i = 0; i < sections_count; i++) + append(alternating[i % alternating.Length]); + }); + + void step(int scrollIndex) + { + AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex])); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]); + } + + for (int i = 1; i < sections_count; i++) + step(i); + for (int i = sections_count - 2; i >= 0; i--) + step(i); + + AddStep("scroll almost to end", () => container.ScrollTo(container.Children[sections_count - 2])); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 2]); + AddStep("scroll down", () => triggerUserScroll(-1)); + AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[sections_count - 1]); + } + + private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(Color4.Yellow, Color4.Gold); + private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray); + + private void append(float multiplier) + { + container.Add(new TestSection + { + Width = 300, + Height = (container.ChildSize.Y - header_height) * multiplier, + Colour = default_colour + }); + } + + private void triggerUserScroll(float direction) + { + InputManager.MoveMouseTo(container); + InputManager.ScrollVerticalBy(direction); + } + + private class TestSection : Box + { + public bool Selected + { + set => Colour = value ? selected_colour : default_colour; + } + } + } +} From 78c14fd69693f688f40595699eba849486f49463 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:36:44 +0200 Subject: [PATCH 015/385] Refactor code into UserTrackingScrollContainer --- .../Graphics/Containers/SectionsContainer.cs | 4 +- .../Containers/UserTrackingScrollContainer.cs | 49 +++++++++++++++++++ osu.Game/Overlays/OverlayScrollContainer.cs | 4 +- osu.Game/Overlays/UserProfileOverlay.cs | 2 +- osu.Game/Screens/Select/BeatmapCarousel.cs | 20 +------- 5 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 81968de304..6e9520ef8f 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -95,7 +95,7 @@ namespace osu.Game.Graphics.Containers protected override Container Content => scrollContentContainer; - private readonly OsuScrollContainer scrollContainer; + private readonly UserTrackingScrollContainer scrollContainer; private readonly Container headerBackgroundContainer; private readonly MarginPadding originalSectionsMargin; private Drawable expandableHeader, fixedHeader, footer, headerBackground; @@ -139,7 +139,7 @@ namespace osu.Game.Graphics.Containers public void ScrollToTop() => scrollContainer.ScrollTo(0); [NotNull] - protected virtual OsuScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + protected virtual UserTrackingScrollContainer CreateScrollContainer() => new UserTrackingScrollContainer(); [NotNull] protected virtual FlowContainer CreateScrollContentContainer() => diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs new file mode 100644 index 0000000000..b8ce34b204 --- /dev/null +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; + +namespace osu.Game.Graphics.Containers +{ + public class UserTrackingScrollContainer : UserTrackingScrollContainer + { + public UserTrackingScrollContainer() + { + } + + public UserTrackingScrollContainer(Direction direction) + : base(direction) + { + } + } + + public class UserTrackingScrollContainer : OsuScrollContainer + where T : Drawable + { + /// + /// Whether the last scroll event was user triggered, directly on the scroll container. + /// + public bool UserScrolling { get; private set; } + + public UserTrackingScrollContainer() + { + } + + public UserTrackingScrollContainer(Direction direction) + : base(direction) + { + } + + protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) + { + UserScrolling = true; + base.OnUserScroll(value, animated, distanceDecay); + } + + public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) + { + UserScrolling = false; + base.ScrollTo(value, animated, distanceDecay); + } + } +} diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index b67d5db1a4..0004719b87 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -17,9 +17,9 @@ using osuTK.Graphics; namespace osu.Game.Overlays { /// - /// which provides . Mostly used in . + /// which provides . Mostly used in . /// - public class OverlayScrollContainer : OsuScrollContainer + public class OverlayScrollContainer : UserTrackingScrollContainer { /// /// Scroll position at which the will be shown. diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 81027667fa..7f29545c2e 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -202,7 +202,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both; } - protected override OsuScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); + protected override UserTrackingScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); protected override FlowContainer CreateScrollContentContainer() => new FillFlowContainer { diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index d76f0abb9e..e9a8d28316 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -901,15 +901,10 @@ namespace osu.Game.Screens.Select } } - protected class CarouselScrollContainer : OsuScrollContainer + protected class CarouselScrollContainer : UserTrackingScrollContainer { private bool rightMouseScrollBlocked; - /// - /// Whether the last scroll event was user triggered, directly on the scroll container. - /// - public bool UserScrolling { get; private set; } - public CarouselScrollContainer() { // size is determined by the carousel itself, due to not all content necessarily being loaded. @@ -919,19 +914,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; - base.OnUserScroll(value, animated, distanceDecay); - } - - public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) - { - UserScrolling = false; - base.ScrollTo(value, animated, distanceDecay); - } - protected override bool OnMouseDown(MouseDownEvent e) { if (e.Button == MouseButton.Right) From 2cf76ebc75c6e0dc4bfbe428a9ee099c21a5eeb9 Mon Sep 17 00:00:00 2001 From: Endrik Tombak Date: Tue, 22 Dec 2020 17:51:12 +0200 Subject: [PATCH 016/385] Scroll to 20% and select section intersecting below there --- .../Graphics/Containers/SectionsContainer.cs | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 6e9520ef8f..b5f81c516a 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Layout; +using osu.Framework.Utils; namespace osu.Game.Graphics.Containers { @@ -20,6 +21,8 @@ namespace osu.Game.Graphics.Containers where T : Drawable { public Bindable SelectedSection { get; } = new Bindable(); + private Drawable lastClickedSection; + private T smallestSection; public Drawable ExpandableHeader { @@ -131,10 +134,21 @@ namespace osu.Game.Graphics.Containers lastKnownScroll = float.NaN; headerHeight = float.NaN; footerHeight = float.NaN; + + if (drawable == null) + return; + + if (smallestSection == null || smallestSection.Height > drawable.Height) + smallestSection = drawable; } - public void ScrollTo(Drawable section) => - scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - (FixedHeader?.BoundingBox.Height ?? 0)); + private const float scroll_target_multiplier = 0.2f; + + public void ScrollTo(Drawable section) + { + lastClickedSection = section; + scrollContainer.ScrollTo(scrollContainer.GetChildPosInContent(section) - scrollContainer.DisplayableContent * scroll_target_multiplier - (FixedHeader?.BoundingBox.Height ?? 0)); + } public void ScrollToTop() => scrollContainer.ScrollTo(0); @@ -183,6 +197,10 @@ namespace osu.Game.Graphics.Containers { lastKnownScroll = currentScroll; + // reset last clicked section because user started scrolling themselves + if (scrollContainer.UserScrolling) + lastClickedSection = null; + if (ExpandableHeader != null && FixedHeader != null) { float offset = Math.Min(ExpandableHeader.LayoutSize.Y, currentScroll); @@ -194,18 +212,27 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0); headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - float scrollOffset = FixedHeader?.LayoutSize.Y ?? 0; + // scroll offset is our fixed header height if we have it plus 20% of content height + // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards + // but the 5% can't be bigger than our smallest section height, otherwise it won't get selected correctly + float sectionOrContent = Math.Min(smallestSection?.Height / 2.0f ?? 0, scrollContainer.DisplayableContent * 0.05f); + float scrollOffset = (FixedHeader?.LayoutSize.Y ?? 0) + scrollContainer.DisplayableContent * scroll_target_multiplier + sectionOrContent; Func diff = section => scrollContainer.GetChildPosInContent(section) - currentScroll - scrollOffset; - if (scrollContainer.IsScrolledToEnd()) + if (Precision.AlmostBigger(0, scrollContainer.Current)) { - SelectedSection.Value = Children.LastOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? Children.FirstOrDefault(); + return; } - else + + if (Precision.AlmostBigger(scrollContainer.Current, scrollContainer.ScrollableExtent)) { - SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() - ?? Children.FirstOrDefault(); + SelectedSection.Value = lastClickedSection as T ?? Children.LastOrDefault(); + return; } + + SelectedSection.Value = Children.TakeWhile(section => diff(section) <= 0).LastOrDefault() + ?? Children.FirstOrDefault(); } } From 6a80e1303d540187093c794302f5982fa171ae0e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Wed, 23 Dec 2020 12:56:04 +0100 Subject: [PATCH 017/385] LINQ-ify Import() logic and ignore case of file extensions. --- osu.Game/OsuGameBase.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ca87772209..7def93255b 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -397,18 +397,13 @@ namespace osu.Game public virtual async Task Import(params ImportTask[] tasks) { - var importTasks = new List(); - - foreach (var extension in tasks.Select(t => Path.GetExtension(t.Path)).Distinct()) + var extensions = tasks.Select(t => Path.GetExtension(t.Path).ToLowerInvariant()).Distinct(); + await Task.WhenAll(extensions.Select(ext => { - var importList = tasks.Where(t => t.Path.EndsWith(extension, StringComparison.OrdinalIgnoreCase)); - var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(extension)); + var imports = tasks.Where(t => t.Path.EndsWith(ext, StringComparison.OrdinalIgnoreCase)); - if (importer != null) - importTasks.Add(importer.Import(importList.ToArray())); - } - - await Task.WhenAll(importTasks); + return fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(ext))?.Import(imports.ToArray()) ?? Task.CompletedTask; + })); } public IEnumerable HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions); From e9e0e18dc53a83c18c6aff7a2aac27b92445c1cb Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 27 Dec 2020 14:11:30 +0100 Subject: [PATCH 018/385] Fix missed change in merge conflict... --- osu.Android/OsuGameActivity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index c397608bc6..7f2ef82d12 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -54,7 +54,7 @@ namespace osu.Android { case Intent.ActionDefault: if (intent.Scheme == ContentResolver.SchemeContent) - handleImportFromUri(intent.Data); + handleImportFromUris(intent.Data); else if (osu_url_schemes.Contains(intent.Scheme)) game.HandleLink(intent.DataString); break; From 013b9b62a1ff7dfd22db139d0571259bcd89de43 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Tue, 29 Dec 2020 20:22:56 +0600 Subject: [PATCH 019/385] add SV multipliers to taiko difficulty mods --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 9 +++++++++ osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 9 +++++++++ osu.Game/Rulesets/Mods/ModHardRock.cs | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index d1ad4c9d8d..5ff91eec9f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.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 osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods @@ -8,5 +9,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModEasy : ModEasy { public override string Description => @"Beats move slower, and less accuracy required!"; + + private const double slider_multiplier = 0.8; + + public override void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + base.ApplyToDifficulty(difficulty); + difficulty.SliderMultiplier *= slider_multiplier; + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 49d225cdb5..37c8dab2de 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.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 osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods @@ -9,5 +10,13 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override double ScoreMultiplier => 1.06; public override bool Ranked => true; + + private const double slider_multiplier = 1.87; + + public override void ApplyToDifficulty(BeatmapDifficulty difficulty) + { + base.ApplyToDifficulty(difficulty); + difficulty.SliderMultiplier *= slider_multiplier; + } } } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 0e589735c1..4edcb0b074 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods { } - public void ApplyToDifficulty(BeatmapDifficulty difficulty) + public virtual void ApplyToDifficulty(BeatmapDifficulty difficulty) { const float ratio = 1.4f; difficulty.CircleSize = Math.Min(difficulty.CircleSize * 1.3f, 10.0f); // CS uses a custom 1.3 ratio. From 669c42a38d7f3ac2d015754166b7af7aacbd13c9 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Wed, 30 Dec 2020 20:57:41 +0600 Subject: [PATCH 020/385] add remarks explaining HR SV multiplier --- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 3 +++ osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 5ff91eec9f..ad6fdf59e2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -10,6 +10,9 @@ namespace osu.Game.Rulesets.Taiko.Mods { public override string Description => @"Beats move slower, and less accuracy required!"; + /// + /// Multiplier factor added to the scrolling speed. + /// private const double slider_multiplier = 0.8; public override void ApplyToDifficulty(BeatmapDifficulty difficulty) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 37c8dab2de..a5a8b75f80 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -11,7 +11,15 @@ namespace osu.Game.Rulesets.Taiko.Mods public override double ScoreMultiplier => 1.06; public override bool Ranked => true; - private const double slider_multiplier = 1.87; + /// + /// Multiplier factor added to the scrolling speed. + /// + /// + /// This factor is made up of two parts: the base part (1.4) and the aspect ratio adjustment (4/3). + /// Stable applies the latter by dividing the width of the user's display by the width of a display with the same height, but 4:3 aspect ratio. + /// TODO: Revisit if taiko playfield ever changes away from a hard-coded 16:9 (see https://github.com/ppy/osu/issues/5685). + /// + private const double slider_multiplier = 1.4 * 4 / 3; public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { From 4266c67bb66dc58fcdbc4406afe38a7d47c392d9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 2 Jan 2021 19:19:55 +0100 Subject: [PATCH 021/385] Remove duplicated code path in switch. --- osu.Android/OsuGameActivity.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 42d9ad33ab..788e5f82be 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -62,13 +62,6 @@ namespace osu.Android break; case Intent.ActionSend: - { - var content = intent.ClipData?.GetItemAt(0); - if (content != null) - handleImportFromUris(content.Uri); - break; - } - case Intent.ActionSendMultiple: { var uris = new List(); From 249be461d511b10f541ef331b3b1aebe666d74be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Jan 2021 11:09:55 +0300 Subject: [PATCH 022/385] Add "explicit maps" search filter control --- .../Online/API/Requests/SearchBeatmapSetsRequest.cs | 8 +++++++- .../BeatmapListing/BeatmapListingFilterControl.cs | 4 +++- .../BeatmapListing/BeatmapListingSearchControl.cs | 6 +++++- osu.Game/Overlays/BeatmapListing/SearchExplicit.cs | 11 +++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Overlays/BeatmapListing/SearchExplicit.cs diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index bbaa7e745f..939d3c6cb4 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -30,6 +30,8 @@ namespace osu.Game.Online.API.Requests public SearchPlayed Played { get; } + public SearchExplicit Explicit { get; } + [CanBeNull] public IReadOnlyCollection Ranks { get; } @@ -50,7 +52,8 @@ namespace osu.Game.Online.API.Requests SearchLanguage language = SearchLanguage.Any, IReadOnlyCollection extra = null, IReadOnlyCollection ranks = null, - SearchPlayed played = SearchPlayed.Any) + SearchPlayed played = SearchPlayed.Any, + SearchExplicit explicitMaps = SearchExplicit.Hide) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; @@ -64,6 +67,7 @@ namespace osu.Game.Online.API.Requests Extra = extra; Ranks = ranks; Played = played; + Explicit = explicitMaps; } protected override WebRequest CreateWebRequest() @@ -93,6 +97,8 @@ namespace osu.Game.Online.API.Requests if (Played != SearchPlayed.Any) req.AddParameter("played", Played.ToString().ToLowerInvariant()); + req.AddParameter("nsfw", Explicit == SearchExplicit.Show ? "true" : "false"); + req.AddCursor(cursor); return req; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index d991dcfcfb..650adcb4a9 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -141,6 +141,7 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Extra.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Ranks.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Played.BindValueChanged(_ => queueUpdateSearch()); + searchControl.Explicit.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); @@ -193,7 +194,8 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Language.Value, searchControl.Extra, searchControl.Ranks, - searchControl.Played.Value); + searchControl.Played.Value, + searchControl.Explicit.Value); getSetsRequest.Success += response => { diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index e232bf045f..c14d693f7d 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -42,6 +42,8 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable Played => playedFilter.Current; + public Bindable Explicit => explicitFilter.Current; + public BeatmapSetInfo BeatmapSet { set @@ -65,6 +67,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchMultipleSelectionFilterRow extraFilter; private readonly BeatmapSearchScoreFilterRow ranksFilter; private readonly BeatmapSearchFilterRow playedFilter; + private readonly BeatmapSearchFilterRow explicitFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -125,7 +128,8 @@ namespace osu.Game.Overlays.BeatmapListing languageFilter = new BeatmapSearchFilterRow(@"Language"), extraFilter = new BeatmapSearchMultipleSelectionFilterRow(@"Extra"), ranksFilter = new BeatmapSearchScoreFilterRow(), - playedFilter = new BeatmapSearchFilterRow(@"Played") + playedFilter = new BeatmapSearchFilterRow(@"Played"), + explicitFilter = new BeatmapSearchFilterRow(@"Explicit Maps"), } } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs new file mode 100644 index 0000000000..3e57cdd48c --- /dev/null +++ b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Overlays.BeatmapListing +{ + public enum SearchExplicit + { + Hide, + Show + } +} From 24c18397397648894f0b943ec98e2f63735c71cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Jan 2021 11:10:25 +0300 Subject: [PATCH 023/385] Add global web setting for allowing explicit content --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ .../BeatmapListing/BeatmapListingSearchControl.cs | 12 +++++++++++- .../Overlays/Settings/Sections/Online/WebSettings.cs | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index eb34a0885d..5e7a843baf 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -60,6 +60,8 @@ namespace osu.Game.Configuration Set(OsuSetting.ExternalLinkWarning, true); Set(OsuSetting.PreferNoVideo, false); + Set(OsuSetting.AllowExplicitContent, false); + // Audio Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); @@ -270,5 +272,6 @@ namespace osu.Game.Configuration EditorWaveformOpacity, DiscordRichPresence, AutomaticallyDownloadWhenSpectating, + AllowExplicitContent, } } diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index c14d693f7d..3761aee312 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Input.Events; using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osuTK.Graphics; @@ -140,10 +141,19 @@ namespace osu.Game.Overlays.BeatmapListing categoryFilter.Current.Value = SearchCategory.Leaderboard; } + private IBindable allowExplicitContent; + [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, OsuConfigManager config) { background.Colour = colourProvider.Dark6; + + allowExplicitContent = config.GetBindable(OsuSetting.AllowExplicitContent); + allowExplicitContent.BindValueChanged(allow => + { + // Update search control if global "explicit allowed" setting changed. + Explicit.Value = allow.NewValue ? SearchExplicit.Show : SearchExplicit.Hide; + }, true); } public void TakeFocus() => textBox.TakeFocus(); diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 8134c350a6..da7ef46f65 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -33,6 +33,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online Keywords = new[] { "spectator" }, Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), }, + new SettingsCheckbox + { + LabelText = "Hide warnings for explicit content in beatmaps", + Keywords = new[] { "nsfw", "18+", "offensive" }, + Current = config.GetBindable(OsuSetting.AllowExplicitContent), + } }; } } From 80fa2cf69330ff805c1de0f4464682a8f79b5156 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Jan 2021 11:14:05 +0300 Subject: [PATCH 024/385] Add test coverage --- .../TestSceneBeatmapListingSearchControl.cs | 71 +++++++++++++------ 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index 3f757031f8..cb86047dea 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; @@ -19,9 +20,18 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); - private readonly BeatmapListingSearchControl control; + private BeatmapListingSearchControl control; - public TestSceneBeatmapListingSearchControl() + private OsuConfigManager localConfig; + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage)); + } + + [SetUp] + public void SetUp() => Schedule(() => { OsuSpriteText query; OsuSpriteText ruleset; @@ -31,30 +41,34 @@ namespace osu.Game.Tests.Visual.UserInterface OsuSpriteText extra; OsuSpriteText ranks; OsuSpriteText played; + OsuSpriteText explicitMap; - Add(control = new BeatmapListingSearchControl + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }); - - Add(new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - Children = new Drawable[] + control = new BeatmapListingSearchControl { - query = new OsuSpriteText(), - ruleset = new OsuSpriteText(), - category = new OsuSpriteText(), - genre = new OsuSpriteText(), - language = new OsuSpriteText(), - extra = new OsuSpriteText(), - ranks = new OsuSpriteText(), - played = new OsuSpriteText() + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + query = new OsuSpriteText(), + ruleset = new OsuSpriteText(), + category = new OsuSpriteText(), + genre = new OsuSpriteText(), + language = new OsuSpriteText(), + extra = new OsuSpriteText(), + ranks = new OsuSpriteText(), + played = new OsuSpriteText(), + explicitMap = new OsuSpriteText(), + } } - }); + }; control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true); control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true); @@ -64,7 +78,8 @@ namespace osu.Game.Tests.Visual.UserInterface control.Extra.BindCollectionChanged((u, v) => extra.Text = $"Extra: {(control.Extra.Any() ? string.Join('.', control.Extra.Select(i => i.ToString().ToLowerInvariant())) : "")}", true); control.Ranks.BindCollectionChanged((u, v) => ranks.Text = $"Ranks: {(control.Ranks.Any() ? string.Join('.', control.Ranks.Select(i => i.ToString())) : "")}", true); control.Played.BindValueChanged(p => played.Text = $"Played: {p.NewValue}", true); - } + control.Explicit.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true); + }); [Test] public void TestCovers() @@ -74,6 +89,16 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Set null beatmap", () => control.BeatmapSet = null); } + [Test] + public void TestExplicitConfig() + { + AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.AllowExplicitContent, true)); + AddAssert("explicit control set to show", () => control.Explicit.Value == SearchExplicit.Show); + + AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.AllowExplicitContent, false)); + AddAssert("explicit control set to hide", () => control.Explicit.Value == SearchExplicit.Hide); + } + private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo From 5f10bcce02411a22f783ba3a5bbb9d7ab66f43af Mon Sep 17 00:00:00 2001 From: Mysfit Date: Wed, 13 Jan 2021 00:09:22 -0500 Subject: [PATCH 025/385] Added beatmap colour settings checkbox and associated tests. --- .../TestSceneLegacyBeatmapSkin.cs | 78 +++++++++++++++++-- .../TestSceneSkinFallbacks.cs | 25 ++++++ osu.Game/Configuration/OsuConfigManager.cs | 2 + .../Overlays/Settings/Sections/SkinSection.cs | 5 ++ .../Play/PlayerSettings/VisualSettings.cs | 3 + .../Skinning/BeatmapSkinProvidingContainer.cs | 7 +- 6 files changed, 110 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index a768626005..83b1e28476 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -7,9 +7,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Play; using osu.Game.Skinning; @@ -24,34 +27,81 @@ namespace osu.Game.Rulesets.Osu.Tests [Resolved] private AudioManager audio { get; set; } - [TestCase(true)] - [TestCase(false)] - public void TestBeatmapComboColours(bool customSkinColoursPresent) + private readonly Bindable beatmapSkins = new Bindable(); + private readonly Bindable beatmapColours = new Bindable(); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); + config.BindWith(OsuSetting.BeatmapColours, beatmapColours); + } + + [TestCase(true, true, true)] + [TestCase(true, false, true)] + [TestCase(false, true, true)] + [TestCase(false, false, true)] + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin, bool useBeatmapColour) { ExposedPlayer player = null; - AddStep("load coloured beatmap", () => player = loadBeatmap(customSkinColoursPresent, true)); + configureSettings(useBeatmapSkin, useBeatmapColour); + AddStep("load coloured beatmap", () => player = loadBeatmap(userHasCustomColours, true)); AddUntilStep("wait for player", () => player.IsLoaded); AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } - [Test] - public void TestBeatmapNoComboColours() + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin, bool useBeatmapColour) { ExposedPlayer player = null; + configureSettings(useBeatmapSkin, useBeatmapColour); + AddStep("load coloured beatmap", () => player = loadBeatmap(true, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is user custom skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + } + + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin, bool useBeatmapColour) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, useBeatmapColour); + AddStep("load coloured beatmap", () => player = loadBeatmap(false, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + } + + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, useBeatmapColour); AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false)); AddUntilStep("wait for player", () => player.IsLoaded); AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } - [Test] - public void TestBeatmapNoComboColoursSkinOverride() + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { ExposedPlayer player = null; + configureSettings(useBeatmapSkin, useBeatmapColour); AddStep("load custom-skin colour", () => player = loadBeatmap(true, false)); AddUntilStep("wait for player", () => player.IsLoaded); @@ -69,6 +119,18 @@ namespace osu.Game.Rulesets.Osu.Tests return player; } + private void configureSettings(bool beatmapSkins, bool beatmapColours) + { + AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () => + { + this.beatmapSkins.Value = beatmapSkins; + }); + AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () => + { + this.beatmapColours.Value = beatmapColours; + }); + } + private class ExposedPlayer : Player { private readonly bool userHasCustomColours; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 856bfd7e80..10baca438d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -52,6 +52,31 @@ namespace osu.Game.Rulesets.Osu.Tests checkNextHitObject(null); } + [Test] + public void TestBeatmapColourDefault() + { + AddStep("enable user provider", () => testUserSkin.Enabled = true); + + AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); + AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true)); + checkNextHitObject("beatmap"); + + AddStep("enable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, true)); + AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false)); + checkNextHitObject("beatmap"); + + AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); + AddStep("enable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, true)); + checkNextHitObject("user"); + + AddStep("disable beatmap skin", () => LocalConfig.Set(OsuSetting.BeatmapSkins, false)); + AddStep("disable beatmap colours", () => LocalConfig.Set(OsuSetting.BeatmapColours, false)); + checkNextHitObject("user"); + + AddStep("disable user provider", () => testUserSkin.Enabled = false); + checkNextHitObject(null); + } + private void checkNextHitObject(string skin) => AddUntilStep($"check skin from {skin}", () => { diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index eb34a0885d..5a8f7b4477 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -82,6 +82,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowStoryboard, true); Set(OsuSetting.BeatmapSkins, true); + Set(OsuSetting.BeatmapColours, true); Set(OsuSetting.BeatmapHitsounds, true); Set(OsuSetting.CursorRotation, true); @@ -250,6 +251,7 @@ namespace osu.Game.Configuration ScreenshotCaptureMenuCursor, SongSelectRightMouseScroll, BeatmapSkins, + BeatmapColours, BeatmapHitsounds, IncreaseFirstObjectVisibility, ScoreDisplayMode, diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5898482e4a..e29f97c33e 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -70,6 +70,11 @@ namespace osu.Game.Overlays.Settings.Sections Current = config.GetBindable(OsuSetting.BeatmapSkins) }, new SettingsCheckbox + { + LabelText = "Beatmap colours", + Current = config.GetBindable(OsuSetting.BeatmapColours) + }, + new SettingsCheckbox { LabelText = "Beatmap hitsounds", Current = config.GetBindable(OsuSetting.BeatmapHitsounds) diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 8f29fe7893..a97078c461 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -14,6 +14,7 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerSliderBar blurSliderBar; private readonly PlayerCheckbox showStoryboardToggle; private readonly PlayerCheckbox beatmapSkinsToggle; + private readonly PlayerCheckbox beatmapColorsToggle; private readonly PlayerCheckbox beatmapHitsoundsToggle; public VisualSettings() @@ -43,6 +44,7 @@ namespace osu.Game.Screens.Play.PlayerSettings }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, + beatmapColorsToggle = new PlayerCheckbox { LabelText = "Beatmap colours" }, beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } }; } @@ -54,6 +56,7 @@ namespace osu.Game.Screens.Play.PlayerSettings blurSliderBar.Current = config.GetBindable(OsuSetting.BlurLevel); showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard); beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins); + beatmapColorsToggle.Current = config.GetBindable(OsuSetting.BeatmapColours); beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); } } diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs index fc01f0bd31..ffc35b3ab6 100644 --- a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -15,6 +15,7 @@ namespace osu.Game.Skinning public class BeatmapSkinProvidingContainer : SkinProvidingContainer { private Bindable beatmapSkins; + private Bindable beatmapColours; private Bindable beatmapHitsounds; protected override bool AllowConfigurationLookup @@ -24,10 +25,10 @@ namespace osu.Game.Skinning if (beatmapSkins == null) throw new InvalidOperationException($"{nameof(BeatmapSkinProvidingContainer)} needs to be loaded before being consumed."); - return beatmapSkins.Value; + return beatmapColours.Value; } } - + protected override bool AllowDrawableLookup(ISkinComponent component) { if (beatmapSkins == null) @@ -62,6 +63,7 @@ namespace osu.Game.Skinning var config = parent.Get(); beatmapSkins = config.GetBindable(OsuSetting.BeatmapSkins); + beatmapColours = config.GetBindable(OsuSetting.BeatmapColours); beatmapHitsounds = config.GetBindable(OsuSetting.BeatmapHitsounds); return base.CreateChildDependencies(parent); @@ -71,6 +73,7 @@ namespace osu.Game.Skinning private void load() { beatmapSkins.BindValueChanged(_ => TriggerSourceChanged()); + beatmapColours.BindValueChanged(_ => TriggerSourceChanged()); beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged()); } } From 7bfb5954a8e7553dbd429f5901865525045d809a Mon Sep 17 00:00:00 2001 From: Mysfit Date: Wed, 13 Jan 2021 00:25:54 -0500 Subject: [PATCH 026/385] Fix whitespace formatting. --- osu.Game/Skinning/BeatmapSkinProvidingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs index ffc35b3ab6..c16547589d 100644 --- a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Skinning return beatmapColours.Value; } } - + protected override bool AllowDrawableLookup(ISkinComponent component) { if (beatmapSkins == null) From 80bcd78a4802131d02576bebcf87c16f8297f2af Mon Sep 17 00:00:00 2001 From: Mysfit Date: Wed, 13 Jan 2021 02:04:59 -0500 Subject: [PATCH 027/385] Removed unnecessary using. --- osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 83b1e28476..138182c7c4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -12,7 +12,6 @@ using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Play; using osu.Game.Skinning; From e8daea91d22e87e95d2deaf8caf70817c6277ebc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Jan 2021 18:13:05 +0300 Subject: [PATCH 028/385] Add online beatmap "explicit content" property --- osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs | 5 +++++ osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 06dee4d3f5..48f1f0ce68 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -31,6 +31,11 @@ namespace osu.Game.Beatmaps /// public BeatmapSetOnlineStatus Status { get; set; } + /// + /// Whether or not this beatmap set has explicit content. + /// + public bool HasExplicitContent { get; set; } + /// /// Whether or not this beatmap set has a background video. /// diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 720d6bfff4..bd1800e9f7 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -42,6 +42,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"bpm")] private double bpm { get; set; } + [JsonProperty(@"nsfw")] + private bool hasExplicitContent { get; set; } + [JsonProperty(@"video")] private bool hasVideo { get; set; } @@ -94,6 +97,7 @@ namespace osu.Game.Online.API.Requests.Responses FavouriteCount = favouriteCount, BPM = bpm, Status = Status, + HasExplicitContent = hasExplicitContent, HasVideo = hasVideo, HasStoryboard = hasStoryboard, Submitted = submitted, From ee6baeb57e0d51e8d65a3c8c70e25701cc58ada8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Jan 2021 18:15:00 +0300 Subject: [PATCH 029/385] Add "explicit" marker pill --- .../BeatmapSet/ExplicitBeatmapPill.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs new file mode 100644 index 0000000000..77528c65c3 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class ExplicitBeatmapPill : CompositeDrawable + { + public ExplicitBeatmapPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OverlayColourProvider colourProvider) + { + InternalChild = new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background5 ?? colours.Gray2, + }, + new OsuSpriteText + { + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = "EXPLICIT", + Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold), + // todo: this is --hsl-orange-2 from the new palette in https://github.com/ppy/osu-web/blob/8ceb46f/resources/assets/less/colors.less#L128-L151, + // should probably take the whole palette from there onto OsuColour for a nicer look in code. + Colour = Color4.FromHsl(new Vector4(45f / 360, 0.8f, 0.6f, 1f)), + } + } + }; + } + } +} From f6637eec36a460c5551e81d5b8776c12fad0caa9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 11:29:30 +0300 Subject: [PATCH 030/385] Add explicit pill to beatmap panels --- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 29 +++++++++++++++---- .../BeatmapListing/Panels/ListBeatmapPanel.cs | 27 ++++++++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 28c36e6c56..b7002a96e5 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.BeatmapSet; using osuTK; using osuTK.Graphics; @@ -24,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float horizontal_padding = 10; private const float vertical_padding = 5; - private FillFlowContainer bottomPanel, statusContainer; + private FillFlowContainer bottomPanel, statusContainer, titleContainer; private PlayButton playButton; private Box progressBar; @@ -73,12 +74,20 @@ namespace osu.Game.Overlays.BeatmapListing.Panels AutoSizeAxes = Axes.Both, Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding }, Direction = FillDirection.Vertical, - Children = new[] + Children = new Drawable[] { - new OsuSpriteText + titleContainer = new FillFlowContainer { - Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) + }, + } }, new OsuSpriteText { @@ -194,6 +203,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); + if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) + { + titleContainer.Add(new ExplicitBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film)); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 433ea37f06..69671ab75b 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.BeatmapSet; using osuTK; using osuTK.Graphics; @@ -26,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float vertical_padding = 5; private const float height = 70; - private FillFlowContainer statusContainer; + private FillFlowContainer statusContainer, titleContainer; protected BeatmapPanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -98,10 +99,18 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + titleContainer = new FillFlowContainer { - Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), - Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) + }, + } }, new OsuSpriteText { @@ -208,6 +217,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); + if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) + { + titleContainer.Add(new ExplicitBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); From 78631323ba35174543cbb09d2c8fd4f545888077 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 11:32:36 +0300 Subject: [PATCH 031/385] Add explicit pill to beatmap overlay --- osu.Game/Overlays/BeatmapSet/Header.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 321e496511..fbdee91d42 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -34,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box coverGradient; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; + private readonly ExplicitBeatmapPill explicitPill; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; @@ -146,6 +147,13 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Margin = new MarginPadding { Left = 3, Bottom = 4 }, // To better lineup with the font }, + explicitPill = new ExplicitBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 4 }, + } } }, artist = new OsuSpriteText @@ -253,6 +261,8 @@ namespace osu.Game.Overlays.BeatmapSet title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; + explicitPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; From 1502b07ea86d7673dbbffa2fc2ec83612ca8d81c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 11:33:00 +0300 Subject: [PATCH 032/385] Add explicit pill to playlist items --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index e3bce4029f..7987d715e3 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -23,6 +23,7 @@ using osu.Game.Online; using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; @@ -41,6 +42,7 @@ namespace osu.Game.Screens.OnlinePlay private Container difficultyIconContainer; private LinkFlowContainer beatmapText; private LinkFlowContainer authorText; + private ExplicitBeatmapPill explicitPill; private ModDisplay modDisplay; private readonly Bindable beatmap = new Bindable(); @@ -116,6 +118,9 @@ namespace osu.Game.Screens.OnlinePlay authorText.AddUserLink(Item.Beatmap.Value?.Metadata.Author); } + bool hasExplicitContent = Item.Beatmap.Value.BeatmapSet.OnlineInfo?.HasExplicitContent == true; + explicitPill.Alpha = hasExplicitContent ? 1 : 0; + modDisplay.Current.Value = requiredMods.ToArray(); } @@ -165,18 +170,37 @@ namespace osu.Game.Screens.OnlinePlay { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(15, 0), + Spacing = new Vector2(10f, 0), Children = new Drawable[] { - authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, - modDisplay = new ModDisplay + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] + { + authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, + explicitPill = new ExplicitBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Top = 3f }, + } + }, + }, + new Container { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Scale = new Vector2(0.4f), - DisplayUnrankedText = false, - ExpansionMode = ExpansionMode.AlwaysExpanded + Child = modDisplay = new ModDisplay + { + Scale = new Vector2(0.4f), + DisplayUnrankedText = false, + ExpansionMode = ExpansionMode.AlwaysExpanded + } } } } From 7fd55efc434fb5522de44429cc27e27c32cd6969 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 11:57:29 +0300 Subject: [PATCH 033/385] Add test cases for displaying explicit beatmaps --- .../Multiplayer/TestSceneDrawableRoomPlaylist.cs | 9 +++++++++ .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 11 +++++++++++ osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs | 7 ++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 65c0cfd328..17d85d1120 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -222,6 +222,15 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); } + [Test] + public void TestExplicitBeatmapItem() + { + var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + beatmap.BeatmapSet.OnlineInfo.HasExplicitContent = true; + + createPlaylist(beatmap); + } + private void moveToItem(int index, Vector2? offset = null) => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType>().ElementAt(index), offset)); diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index c5d1fd6887..689321698a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -235,6 +235,17 @@ namespace osu.Game.Tests.Visual.Online AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); } + [Test] + public void TestExplicitBeatmap() + { + AddStep("show explicit map", () => + { + var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + beatmapSet.OnlineInfo.HasExplicitContent = true; + overlay.ShowBeatmapSet(beatmapSet); + }); + } + [Test] public void TestHide() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 74ece5da05..fd5f306e07 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -99,13 +99,16 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var normal = CreateWorkingBeatmap(Ruleset.Value).BeatmapSetInfo; + var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; normal.OnlineInfo.HasVideo = true; normal.OnlineInfo.HasStoryboard = true; var undownloadable = getUndownloadableBeatmapSet(); var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); + var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + explicitMap.OnlineInfo.HasExplicitContent = true; + Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, @@ -121,9 +124,11 @@ namespace osu.Game.Tests.Visual.Online new GridBeatmapPanel(normal), new GridBeatmapPanel(undownloadable), new GridBeatmapPanel(manyDifficulties), + new GridBeatmapPanel(explicitMap), new ListBeatmapPanel(normal), new ListBeatmapPanel(undownloadable), new ListBeatmapPanel(manyDifficulties), + new ListBeatmapPanel(explicitMap) }, }, }; From f827c620813468f2c670f393c424b84e8b11f634 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 11:58:55 +0300 Subject: [PATCH 034/385] Add empty online info to test beatmap This is for `BeatmapSetOverlay` to not eat a null reference trying to access `Beatmap.OnlineInfo`. --- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 035cb64099..fa6dc5647d 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -32,6 +32,7 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.BeatmapSet.Files = new List(); BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; + BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo { Status = BeatmapSetOnlineStatus.Ranked, From 268f9d661f16c55aa1c8ebadb4f44e05baa7373a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 13:00:14 +0300 Subject: [PATCH 035/385] Dispose of local config on disposal --- .../UserInterface/TestSceneBeatmapListingSearchControl.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index cb86047dea..dc46da6293 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -99,6 +99,12 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("explicit control set to hide", () => control.Explicit.Value == SearchExplicit.Hide); } + protected override void Dispose(bool isDisposing) + { + localConfig?.Dispose(); + base.Dispose(isDisposing); + } + private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo { OnlineInfo = new BeatmapSetOnlineInfo From 9d59d784f8674b1319a175c1edb74d079de76dfe Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 17:06:44 +0300 Subject: [PATCH 036/385] Add Colour{1-4} properties to `OverlayColourProvider` --- osu.Game/Overlays/OverlayColourProvider.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index 9816f313ad..f33223d7aa 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -16,6 +16,11 @@ namespace osu.Game.Overlays this.colourScheme = colourScheme; } + public Color4 Colour1 => getColour(1, 0.7f); + public Color4 Colour2 => getColour(0.8f, 0.6f); + public Color4 Colour3 => getColour(0.6f, 0.5f); + public Color4 Colour4 => getColour(0.4f, 0.3f); + public Color4 Highlight1 => getColour(1, 0.7f); public Color4 Content1 => getColour(0.4f, 1); public Color4 Content2 => getColour(0.4f, 0.9f); From e275dd02e08ab8d9e890d2d0a2b1f822e1c53f6c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 17:07:11 +0300 Subject: [PATCH 037/385] Create static colour properties for now --- osu.Game/Overlays/OverlayColourProvider.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs index f33223d7aa..abd1e43f25 100644 --- a/osu.Game/Overlays/OverlayColourProvider.cs +++ b/osu.Game/Overlays/OverlayColourProvider.cs @@ -11,6 +11,13 @@ namespace osu.Game.Overlays { private readonly OverlayColourScheme colourScheme; + public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red); + public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink); + public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange); + public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green); + public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple); + public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue); + public OverlayColourProvider(OverlayColourScheme colourScheme) { this.colourScheme = colourScheme; From 43daa7c7c09463acac11095bc93f304e1c20ae83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Jan 2021 17:07:42 +0300 Subject: [PATCH 038/385] Use `Colour2` of orange theme for explicit pill --- osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs index 77528c65c3..ad19dffccb 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs @@ -7,8 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet { @@ -38,9 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, Text = "EXPLICIT", Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold), - // todo: this is --hsl-orange-2 from the new palette in https://github.com/ppy/osu-web/blob/8ceb46f/resources/assets/less/colors.less#L128-L151, - // should probably take the whole palette from there onto OsuColour for a nicer look in code. - Colour = Color4.FromHsl(new Vector4(45f / 360, 0.8f, 0.6f, 1f)), + Colour = OverlayColourProvider.Orange.Colour2, } } }; From 1248d39d7e5fa6a7a30271a1346059594e0e021d Mon Sep 17 00:00:00 2001 From: Mysfit Date: Wed, 13 Jan 2021 13:07:07 -0500 Subject: [PATCH 039/385] Reverted change to AllowConfigurationLookup and added a separate AllowColourLookup bool with config case based on lookup type in SkinProvidingContainer GetConfig call. --- .../Skinning/BeatmapSkinProvidingContainer.cs | 11 ++++++ osu.Game/Skinning/SkinProvidingContainer.cs | 34 ++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs index c16547589d..57c08a903f 100644 --- a/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs +++ b/osu.Game/Skinning/BeatmapSkinProvidingContainer.cs @@ -25,6 +25,17 @@ namespace osu.Game.Skinning if (beatmapSkins == null) throw new InvalidOperationException($"{nameof(BeatmapSkinProvidingContainer)} needs to be loaded before being consumed."); + return beatmapSkins.Value; + } + } + + protected override bool AllowColourLookup + { + get + { + if (beatmapColours == null) + throw new InvalidOperationException($"{nameof(BeatmapSkinProvidingContainer)} needs to be loaded before being consumed."); + return beatmapColours.Value; } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index adf62ed452..3232a30110 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -32,6 +32,8 @@ namespace osu.Game.Skinning protected virtual bool AllowConfigurationLookup => true; + protected virtual bool AllowColourLookup => true; + public SkinProvidingContainer(ISkin skin) { this.skin = skin; @@ -68,11 +70,35 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) { - if (AllowConfigurationLookup && skin != null) + if (skin != null) { - var bindable = skin.GetConfig(lookup); - if (bindable != null) - return bindable; + switch (lookup) + { + // todo: the GlobalSkinColours switch is pulled from LegacySkin and should not exist. + // will likely change based on how databased storage of skin configuration goes. + case GlobalSkinColours global: + switch (global) + { + case GlobalSkinColours.ComboColours: + var bindable = skin.GetConfig(lookup); + if (bindable != null && AllowColourLookup) + return bindable; + else + return fallbackSource?.GetConfig(lookup); + } + + break; + + default: + if (AllowConfigurationLookup) + { + var bindable = skin.GetConfig(lookup); + if (bindable != null) + return bindable; + } + + break; + } } return fallbackSource?.GetConfig(lookup); From 8b95817f7abf8f5674ffa11d37c1eb37ce259806 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Wed, 13 Jan 2021 16:05:46 -0500 Subject: [PATCH 040/385] Moved SkinProvidingContainer bindable fetching to common method. Replaced redundant test boolean declarations with inline values. --- .../TestSceneLegacyBeatmapSkin.cs | 28 +++++++++---------- osu.Game/Skinning/SkinProvidingContainer.cs | 24 ++++++++-------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 138182c7c4..22b028906f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -36,41 +36,41 @@ namespace osu.Game.Rulesets.Osu.Tests config.BindWith(OsuSetting.BeatmapColours, beatmapColours); } - [TestCase(true, true, true)] - [TestCase(true, false, true)] - [TestCase(false, true, true)] - [TestCase(false, false, true)] - public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin, bool useBeatmapColour) + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { ExposedPlayer player = null; - configureSettings(useBeatmapSkin, useBeatmapColour); + configureSettings(useBeatmapSkin, true); AddStep("load coloured beatmap", () => player = loadBeatmap(userHasCustomColours, true)); AddUntilStep("wait for player", () => player.IsLoaded); AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } - [TestCase(true, false)] - [TestCase(false, false)] - public void TestBeatmapComboColoursOverride(bool useBeatmapSkin, bool useBeatmapColour) + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { ExposedPlayer player = null; - configureSettings(useBeatmapSkin, useBeatmapColour); + configureSettings(useBeatmapSkin, false); AddStep("load coloured beatmap", () => player = loadBeatmap(true, true)); AddUntilStep("wait for player", () => player.IsLoaded); AddAssert("is user custom skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); } - [TestCase(true, false)] - [TestCase(false, false)] - public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin, bool useBeatmapColour) + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { ExposedPlayer player = null; - configureSettings(useBeatmapSkin, useBeatmapColour); + configureSettings(useBeatmapSkin, false); AddStep("load coloured beatmap", () => player = loadBeatmap(false, true)); AddUntilStep("wait for player", () => player.IsLoaded); diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 3232a30110..ae9a84932a 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -80,30 +80,28 @@ namespace osu.Game.Skinning switch (global) { case GlobalSkinColours.ComboColours: - var bindable = skin.GetConfig(lookup); - if (bindable != null && AllowColourLookup) - return bindable; - else - return fallbackSource?.GetConfig(lookup); + return getBindable(lookup, AllowColourLookup); } break; default: - if (AllowConfigurationLookup) - { - var bindable = skin.GetConfig(lookup); - if (bindable != null) - return bindable; - } - - break; + return getBindable(lookup, AllowConfigurationLookup); } } return fallbackSource?.GetConfig(lookup); } + private IBindable getBindable(TLookup lookup, bool bindableReturnCheck) + { + var bindable = skin.GetConfig(lookup); + if (bindable != null && bindableReturnCheck) + return bindable; + else + return fallbackSource?.GetConfig(lookup); + } + protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From 562634dfd2de90736dfb812c9d8834c0c97bcb4c Mon Sep 17 00:00:00 2001 From: Jesse Myers Date: Wed, 13 Jan 2021 16:49:14 -0500 Subject: [PATCH 041/385] Improve naming around the config lookup with fallback private method. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Skinning/SkinProvidingContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index ae9a84932a..2dc19667e4 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -93,10 +93,10 @@ namespace osu.Game.Skinning return fallbackSource?.GetConfig(lookup); } - private IBindable getBindable(TLookup lookup, bool bindableReturnCheck) + private IBindable lookupWithFallback(TLookup lookup, bool canUseSkinLookup) { var bindable = skin.GetConfig(lookup); - if (bindable != null && bindableReturnCheck) + if (bindable != null && canUseSkinLookup) return bindable; else return fallbackSource?.GetConfig(lookup); From abf718242b11519825228a2f6098dff745231608 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Jan 2021 05:40:43 +0300 Subject: [PATCH 042/385] Make explicit marker font semi-bold --- osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs index ad19dffccb..aefb3299a5 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.BeatmapSet { Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, Text = "EXPLICIT", - Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Colour = OverlayColourProvider.Orange.Colour2, } } From 6281c1086ac377503c89a5648608e47ab6b71e89 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 14 Jan 2021 05:41:09 +0300 Subject: [PATCH 043/385] Space out explicit marker in beatmap overlay --- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index fbdee91d42..9f2b40dfa4 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.BeatmapSet Alpha = 0f, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 10f, Top = 4 }, + Margin = new MarginPadding { Left = 15f, Top = 4 }, } } }, From 99e43c77c23709c90100cea34210c5a2d054a206 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Thu, 14 Jan 2021 16:53:55 -0500 Subject: [PATCH 044/385] Simplified colour config checks in SkinProvidingContainer.cs --- osu.Game/Skinning/SkinProvidingContainer.cs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 2dc19667e4..d2645eff68 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -72,22 +73,12 @@ namespace osu.Game.Skinning { if (skin != null) { - switch (lookup) - { - // todo: the GlobalSkinColours switch is pulled from LegacySkin and should not exist. - // will likely change based on how databased storage of skin configuration goes. - case GlobalSkinColours global: - switch (global) - { - case GlobalSkinColours.ComboColours: - return getBindable(lookup, AllowColourLookup); - } + TValue tValueTypeCheck = default; - break; - - default: - return getBindable(lookup, AllowConfigurationLookup); - } + if (lookup is GlobalSkinColours || tValueTypeCheck is Color4) + return lookupWithFallback(lookup, AllowColourLookup); + else + return lookupWithFallback(lookup, AllowConfigurationLookup); } return fallbackSource?.GetConfig(lookup); From dc8e38cf4d8dfcdb0a33a6d4c73147ce43fe4c05 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 15 Jan 2021 07:20:13 +0300 Subject: [PATCH 045/385] Remove pointless inline comment --- osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 3761aee312..5c6267b726 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -151,7 +151,6 @@ namespace osu.Game.Overlays.BeatmapListing allowExplicitContent = config.GetBindable(OsuSetting.AllowExplicitContent); allowExplicitContent.BindValueChanged(allow => { - // Update search control if global "explicit allowed" setting changed. Explicit.Value = allow.NewValue ? SearchExplicit.Show : SearchExplicit.Hide; }, true); } From 4cccde9007cdb1646f041da71d77184c51f490d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 13:20:46 +0900 Subject: [PATCH 046/385] 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 0c01a3a685c9ba1ca5c29f5e124043780c9f1d61 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Thu, 14 Jan 2021 23:30:24 -0500 Subject: [PATCH 047/385] Found a better solution than TValue type checking for additional beatmap colour settings. Added unit tests for Catch Beatmap Skin settings. --- .../TestSceneLegacyBeatmapSkin.cs | 360 ++++++++++++++++++ osu.Game/Skinning/SkinProvidingContainer.cs | 5 +- 2 files changed, 361 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs new file mode 100644 index 0000000000..12ceaaa6fa --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -0,0 +1,360 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Bindables; +using osu.Framework.IO.Stores; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Play; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestSceneLegacyBeatmapSkin : ScreenTestScene + { + [Resolved] + private AudioManager audio { get; set; } + + private readonly Bindable beatmapSkins = new Bindable(); + private readonly Bindable beatmapColours = new Bindable(); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); + config.BindWith(OsuSetting.BeatmapColours, beatmapColours); + } + + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, true); + AddStep("load coloured beatmap", () => player = loadBeatmap(userHasCustomColours, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, false); + AddStep("load coloured beatmap", () => player = loadBeatmap(true, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is user custom skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, false); + AddStep("load coloured beatmap", () => player = loadBeatmap(false, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + } + + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, useBeatmapColour); + AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + } + + [TestCase(true, true)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(false, false)] + public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, useBeatmapColour); + AddStep("load custom-skin colour", () => player = loadBeatmap(true, false)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapHyperDashColours(bool useBeatmapSkin) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, true); + AddStep("load custom-skin colour", () => player = loadBeatmap(true, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is custom hyper dash colours", () => player.UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => player.UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => player.UsableHyperDashFruitColour == TestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin) + { + ExposedPlayer player = null; + + configureSettings(useBeatmapSkin, false); + AddStep("load custom-skin colour", () => player = loadBeatmap(true, true)); + AddUntilStep("wait for player", () => player.IsLoaded); + + AddAssert("is custom hyper dash colours", () => player.UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => player.UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => player.UsableHyperDashFruitColour == TestSkin.HYPER_DASH_FRUIT_COLOUR); + } + + private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours) + { + ExposedPlayer player; + + Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours); + + LoadScreen(player = new ExposedPlayer(userHasCustomColours)); + + return player; + } + + private void configureSettings(bool beatmapSkins, bool beatmapColours) + { + AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () => + { + this.beatmapSkins.Value = beatmapSkins; + }); + AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () => + { + this.beatmapColours.Value = beatmapColours; + }); + } + + private class ExposedPlayer : Player + { + private readonly bool userHasCustomColours; + + public ExposedPlayer(bool userHasCustomColours) + : base(new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) + { + this.userHasCustomColours = userHasCustomColours; + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(new TestSkin(userHasCustomColours)); + return dependencies; + } + + public IReadOnlyList UsableComboColours => + GameplayClockContainer.ChildrenOfType() + .First() + .GetConfig>(GlobalSkinColours.ComboColours)?.Value; + + public Color4 UsableHyperDashColour => + GameplayClockContainer.ChildrenOfType() + .First() + .GetConfig(new SkinCustomColourLookup(CatchSkinColour.HyperDash))? + .Value ?? Color4.Red; + + public Color4 UsableHyperDashAfterImageColour => + GameplayClockContainer.ChildrenOfType() + .First() + .GetConfig(new SkinCustomColourLookup(CatchSkinColour.HyperDashAfterImage))? + .Value ?? Color4.Red; + + public Color4 UsableHyperDashFruitColour => + GameplayClockContainer.ChildrenOfType() + .First() + .GetConfig(new SkinCustomColourLookup(CatchSkinColour.HyperDashFruit))? + .Value ?? Color4.Red; + } + + private class TestJuiceStream : JuiceStream + { + public TestJuiceStream(float x) + { + X = x; + + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(new Vector2(30, 0)), + }); + } + } + + private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly bool hasColours; + + public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) + : base(createBeatmap(new CatchRuleset().RulesetInfo), null, null, audio) + { + this.hasColours = hasColours; + } + + protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours); + + private static IBeatmap createBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = + { + Ruleset = ruleset, + BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f } + } + }; + + beatmap.ControlPointInfo.Add(0, new TimingControlPoint()); + + // Should produce a hyper-dash (edge case test) + beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 56, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 2008, X = 308, NewCombo = true }); + + double startTime = 3000; + + const float left_x = 0.02f * CatchPlayfield.WIDTH; + const float right_x = 0.98f * CatchPlayfield.WIDTH; + + createObjects(() => new Fruit { X = left_x }); + createObjects(() => new TestJuiceStream(right_x), 1); + createObjects(() => new TestJuiceStream(left_x), 1); + createObjects(() => new Fruit { X = right_x }); + createObjects(() => new Fruit { X = left_x }); + createObjects(() => new Fruit { X = right_x }); + createObjects(() => new TestJuiceStream(left_x), 1); + + beatmap.ControlPointInfo.Add(startTime, new TimingControlPoint + { + BeatLength = 50 + }); + + createObjects(() => new TestJuiceStream(left_x) + { + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(new Vector2(512, 0)) + }) + }, 1); + + return beatmap; + + void createObjects(Func createObject, int count = 3) + { + const float spacing = 140; + + for (int i = 0; i < count; i++) + { + var hitObject = createObject(); + hitObject.StartTime = startTime + i * spacing; + beatmap.HitObjects.Add(hitObject); + } + + startTime += 700; + } + } + } + + private class TestBeatmapSkin : LegacyBeatmapSkin + { + public static Color4[] Colours { get; } = + { + new Color4(50, 100, 150, 255), + new Color4(40, 80, 120, 255), + }; + + public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; + + public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.DarkCyan; + + public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod; + + public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) + : base(beatmap, new ResourceStore(), null) + { + if (hasColours) + { + Configuration.AddComboColours(Colours); + Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); + Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); + Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); + } + } + } + + private class TestSkin : LegacySkin, ISkinSource + { + public static Color4[] Colours { get; } = + { + new Color4(150, 100, 50, 255), + new Color4(20, 20, 20, 255), + }; + + public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; + + public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.LightCoral; + + public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan; + + public TestSkin(bool hasCustomColours) + : base(new SkinInfo(), new ResourceStore(), null, string.Empty) + { + if (hasCustomColours) + { + Configuration.AddComboColours(Colours); + Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); + Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); + Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); + } + } + + public event Action SourceChanged + { + add { } + remove { } + } + } + } +} diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index d2645eff68..e97822b86e 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; -using osuTK.Graphics; namespace osu.Game.Skinning { @@ -73,9 +72,7 @@ namespace osu.Game.Skinning { if (skin != null) { - TValue tValueTypeCheck = default; - - if (lookup is GlobalSkinColours || tValueTypeCheck is Color4) + if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup) return lookupWithFallback(lookup, AllowColourLookup); else return lookupWithFallback(lookup, AllowConfigurationLookup); From ebbc32adfa9c4652cadff32dc5c429af9867ff09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 14:51:26 +0900 Subject: [PATCH 048/385] 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 049/385] 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 050/385] 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 051/385] 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 052/385] 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 053/385] 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 054/385] 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 055/385] 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 056/385] 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 057/385] 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 058/385] 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 04fa32bc34852ba79a249529f627b9a6f8aa6dd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:14:21 +0900 Subject: [PATCH 059/385] Rename and add xmldoc for smooth seeking method --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- .../Timelines/Summary/Parts/MarkerPart.cs | 2 +- .../Compose/Components/BlueprintContainer.cs | 2 +- osu.Game/Screens/Edit/EditorClock.cs | 33 +++++++++++-------- .../Screens/Edit/Timing/ControlPointTable.cs | 2 +- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 35852f60ea..e927951d0a 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Edit EditorBeatmap.Add(hitObject); if (EditorClock.CurrentTime < hitObject.StartTime) - EditorClock.SeekTo(hitObject.StartTime); + EditorClock.SeekSmoothlyTo(hitObject.StartTime); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 9e9ac93d23..5a2214509c 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); - editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength); + editorClock.SeekSmoothlyTo(markerPos / DrawWidth * editorClock.TrackLength); }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 0b45bd5597..5371beac60 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -170,7 +170,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (clickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != clickedBlueprint) return false; - EditorClock?.SeekTo(clickedBlueprint.HitObject.StartTime); + EditorClock?.SeekSmoothlyTo(clickedBlueprint.HitObject.StartTime); return true; } diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 148eef6c93..c651d6a7c4 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Edit if (!snapped || ControlPointInfo.TimingPoints.Count == 0) { - SeekTo(seekTime); + SeekSmoothlyTo(seekTime); return; } @@ -145,11 +145,11 @@ namespace osu.Game.Screens.Edit // Ensure the sought point is within the boundaries seekTime = Math.Clamp(seekTime, 0, TrackLength); - SeekTo(seekTime); + SeekSmoothlyTo(seekTime); } /// - /// The current time of this clock, include any active transform seeks performed via . + /// The current time of this clock, include any active transform seeks performed via . /// public double CurrentTimeAccurate => Transforms.OfType().FirstOrDefault()?.EndValue ?? CurrentTime; @@ -182,6 +182,23 @@ namespace osu.Game.Screens.Edit return underlyingClock.Seek(position); } + /// + /// Seek smoothly to the provided destination. + /// Use to perform an immediate seek. + /// + /// + public void SeekSmoothlyTo(double seekDestination) + { + seekingOrStopped.Value = true; + + if (IsRunning) + Seek(seekDestination); + else + { + transformSeekTo(seekDestination, transform_time, Easing.OutQuint); + } + } + public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments(); double IAdjustableClock.Rate @@ -243,16 +260,6 @@ namespace osu.Game.Screens.Edit } } - public void SeekTo(double seekDestination) - { - seekingOrStopped.Value = true; - - if (IsRunning) - Seek(seekDestination); - else - transformSeekTo(seekDestination, transform_time, Easing.OutQuint); - } - private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None) => this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing)); diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 89d3c36250..e4b9150df1 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -206,7 +206,7 @@ namespace osu.Game.Screens.Edit.Timing Action = () => { selectedGroup.Value = controlGroup; - clock.SeekTo(controlGroup.Time); + clock.SeekSmoothlyTo(controlGroup.Time); }; } From 831c06a3c7c02549fe9bab75ff83afaaf30fa1ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:14:38 +0900 Subject: [PATCH 060/385] Expose and consume boolean covering whether an ongoing smooth seek is running --- .../Edit/Compose/Components/Timeline/Timeline.cs | 10 ++++++---- osu.Game/Screens/Edit/EditorClock.cs | 11 +++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 20836c0e68..7df4f1ae7d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -146,12 +146,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline seekTrackToCurrent(); else if (!editorClock.IsRunning) { - // The track isn't running. There are two cases we have to be wary of: - // 1) The user flick-drags on this timeline: We want the track to follow us - // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time + // The track isn't running. There are three cases we have to be wary of: + // 1) The user flick-drags on this timeline and we are applying an interpolated seek on the clock, until interrupted by 2 or 3. + // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline; clicking a hitobject etc.). We want the timeline to track the clock's time. + // 3) An ongoing seek transform is running from an external seek. We want the timeline to track the clock's time. // The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally - if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime) + // Checking IsSeeking covers the third case, where the transform may not have been applied yet. + if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime && !editorClock.IsSeeking) seekTrackToCurrent(); else scrollToTrackTime(); diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index c651d6a7c4..ec0f5d7154 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -35,6 +35,11 @@ namespace osu.Game.Screens.Edit private readonly Bindable seekingOrStopped = new Bindable(true); + /// + /// Whether a seek is currently in progress. True for the duration of a seek performed via . + /// + public bool IsSeeking { get; private set; } + public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor) : this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor) { @@ -176,7 +181,7 @@ namespace osu.Game.Screens.Edit public bool Seek(double position) { - seekingOrStopped.Value = true; + seekingOrStopped.Value = IsSeeking = true; ClearTransforms(); return underlyingClock.Seek(position); @@ -246,6 +251,8 @@ namespace osu.Game.Screens.Edit { if (seekingOrStopped.Value) { + IsSeeking &= Transforms.Any(); + if (track.Value?.IsRunning != true) { // seeking in the editor can happen while the track isn't running. @@ -256,7 +263,7 @@ namespace osu.Game.Screens.Edit // we are either running a seek tween or doing an immediate seek. // in the case of an immediate seek the seeking bool will be set to false after one update. // this allows for silencing hit sounds and the likes. - seekingOrStopped.Value = Transforms.Any(); + seekingOrStopped.Value = IsSeeking; } } From b5e784ed427a47c93eae96a2a459b6a4b9dc224c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 16:34:28 +0900 Subject: [PATCH 061/385] Fix possibility of crash when selecting a random skin during skin import --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5898482e4a..123cecb0cd 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) configBindable.Value = 0; - configBindable.BindValueChanged(id => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == id.NewValue), true); + configBindable.BindValueChanged(id => Scheduler.AddOnce(updateSelectedSkinFromConfig), true); dropdownBindable.BindValueChanged(skin => { if (skin.NewValue == random_skin_info) @@ -104,6 +104,8 @@ namespace osu.Game.Overlays.Settings.Sections }); } + private void updateSelectedSkinFromConfig() => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == configBindable.Value); + private void updateItems() { skinItems = skins.GetAllUsableSkins(); From 8a6857f151373dc2bb6edeb5239efef9932e447f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:16:12 +0900 Subject: [PATCH 062/385] Add support for playing a SkinnableSample without restarting it --- osu.Game/Skinning/PausableSkinnableSound.cs | 4 ++-- osu.Game/Skinning/SkinnableSound.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index 4b6099e85f..cb5234c847 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -67,7 +67,7 @@ namespace osu.Game.Skinning } } - public override void Play() + public override void Play(bool restart = true) { cancelPendingStart(); RequestedPlaying = true; @@ -75,7 +75,7 @@ namespace osu.Game.Skinning if (samplePlaybackDisabled.Value) return; - base.Play(); + base.Play(restart); } public override void Stop() diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 645c08cd00..b841f99598 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -119,12 +119,13 @@ namespace osu.Game.Skinning /// /// Plays the samples. /// - public virtual void Play() + /// Whether to play the sample from the beginning. + public virtual void Play(bool restart = true) { samplesContainer.ForEach(c => { if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0) - c.Play(); + c.Play(restart); }); } From 767c76921faa69b9f648e14fec3ab95f466abce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:17:10 +0900 Subject: [PATCH 063/385] Adjust transition time of spinner sound --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 1f3bcece0c..4f61ab4a9a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -131,11 +131,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (tracking.NewValue) { spinningSample?.Play(); - spinningSample?.VolumeTo(1, 200); + spinningSample?.VolumeTo(1, 300); } else { - spinningSample?.VolumeTo(0, 200).Finally(_ => spinningSample.Stop()); + spinningSample?.VolumeTo(0, 300).Finally(_ => spinningSample.Stop()); } } From 311f8b70178cb5ebf0fdc23824fdae53b8336a7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:17:51 +0900 Subject: [PATCH 064/385] Only restart spinning sample if it was not already playing --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 4f61ab4a9a..c5ae195274 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { if (tracking.NewValue) { - spinningSample?.Play(); + spinningSample?.Play(!spinningSample.IsPlaying); spinningSample?.VolumeTo(1, 300); } else From 14b33236828438a72b5c6b4acc4edbba9e26d985 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:18:15 +0900 Subject: [PATCH 065/385] Use OnComplete instead of Finally to avoid potentially stopping on aborted transforms --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index c5ae195274..56aedebed3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } else { - spinningSample?.VolumeTo(0, 300).Finally(_ => spinningSample.Stop()); + spinningSample?.VolumeTo(0, 300).OnComplete(_ => spinningSample.Stop()); } } From d6e6b4bbeea9ec532b7d927fa3d38c7e1efbf49f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Jan 2021 17:34:01 +0900 Subject: [PATCH 066/385] Revert forced cloning of ControlPointInfo This reverts commit 3c3e860dbc34d37855b79786a1abb754af1667e8. Closes https://github.com/ppy/osu/issues/11491. --- 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, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index be2006e67a..5435e86dfd 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -50,15 +50,7 @@ namespace osu.Game.Beatmaps IBeatmap IBeatmap.Clone() => Clone(); - public Beatmap Clone() - { - var clone = (Beatmap)MemberwiseClone(); - - clone.ControlPointInfo = ControlPointInfo.CreateCopy(); - // todo: deep clone other elements as required. - - return clone; - } + public Beatmap Clone() => (Beatmap)MemberwiseClone(); } public class Beatmap : Beatmap 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..d25adca92b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -111,6 +111,8 @@ 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 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 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 067/385] 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); } From 112967c1e8d0fd75c24aed6276afd95140687e79 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Fri, 15 Jan 2021 23:46:46 -0500 Subject: [PATCH 068/385] Created base class for testing beatmap colours. --- .../TestSceneLegacyBeatmapSkin.cs | 191 +++++------------- .../TestSceneLegacyBeatmapSkin.cs | 178 +++++----------- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 147 ++++++++++++++ 3 files changed, 245 insertions(+), 271 deletions(-) create mode 100644 osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index 12ceaaa6fa..89298242e5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -2,13 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Bindables; -using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -17,179 +14,114 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; -using osu.Game.Screens.Play; using osu.Game.Skinning; -using osu.Game.Tests.Visual; +using osu.Game.Tests.Beatmaps; using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests { - public class TestSceneLegacyBeatmapSkin : ScreenTestScene + public class TestSceneLegacyBeatmapSkin : LegacyBeatmapSkinColourTest { [Resolved] private AudioManager audio { get; set; } - private readonly Bindable beatmapSkins = new Bindable(); - private readonly Bindable beatmapColours = new Bindable(); - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); - config.BindWith(OsuSetting.BeatmapColours, beatmapColours); + config.BindWith(OsuSetting.BeatmapSkins, BeatmapSkins); + config.BindWith(OsuSetting.BeatmapColours, BeatmapColours); } [TestCase(true, true)] [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, true); - AddStep("load coloured beatmap", () => player = loadBeatmap(userHasCustomColours, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, false); - AddStep("load coloured beatmap", () => player = loadBeatmap(true, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is user custom skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColoursOverride(useBeatmapSkin); + AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, false); - AddStep("load coloured beatmap", () => player = loadBeatmap(false, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } [TestCase(true, true)] [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, useBeatmapColour); - AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } [TestCase(true, true)] [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, useBeatmapColour); - AddStep("load custom-skin colour", () => player = loadBeatmap(true, false)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false); + base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] public void TestBeatmapHyperDashColours(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, true); - AddStep("load custom-skin colour", () => player = loadBeatmap(true, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is custom hyper dash colours", () => player.UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR); - AddAssert("is custom hyper dash after image colours", () => player.UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); - AddAssert("is custom hyper dash fruit colours", () => player.UsableHyperDashFruitColour == TestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + ConfigureTest(useBeatmapSkin, true, true); + AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == CatchTestBeatmapSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == CatchTestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == CatchTestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR); } [TestCase(true)] [TestCase(false)] public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, false); - AddStep("load custom-skin colour", () => player = loadBeatmap(true, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is custom hyper dash colours", () => player.UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR); - AddAssert("is custom hyper dash after image colours", () => player.UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); - AddAssert("is custom hyper dash fruit colours", () => player.UsableHyperDashFruitColour == TestSkin.HYPER_DASH_FRUIT_COLOUR); + TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); + ConfigureTest(useBeatmapSkin, false, true); + AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == CatchTestSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == CatchTestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == CatchTestSkin.HYPER_DASH_FRUIT_COLOUR); } - private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours) + protected override ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new CatchExposedPlayer(userHasCustomColours); + + private class CatchExposedPlayer : ExposedPlayer { - ExposedPlayer player; - - Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours); - - LoadScreen(player = new ExposedPlayer(userHasCustomColours)); - - return player; - } - - private void configureSettings(bool beatmapSkins, bool beatmapColours) - { - AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () => + public CatchExposedPlayer(bool userHasCustomColours) + : base(userHasCustomColours) { - this.beatmapSkins.Value = beatmapSkins; - }); - AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () => - { - this.beatmapColours.Value = beatmapColours; - }); - } - - private class ExposedPlayer : Player - { - private readonly bool userHasCustomColours; - - public ExposedPlayer(bool userHasCustomColours) - : base(new PlayerConfiguration - { - AllowPause = false, - ShowResults = false, - }) - { - this.userHasCustomColours = userHasCustomColours; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(new TestSkin(userHasCustomColours)); + dependencies.CacheAs(new CatchTestSkin(UserHasCustomColours)); return dependencies; } - public IReadOnlyList UsableComboColours => - GameplayClockContainer.ChildrenOfType() - .First() - .GetConfig>(GlobalSkinColours.ComboColours)?.Value; - public Color4 UsableHyperDashColour => GameplayClockContainer.ChildrenOfType() .First() @@ -223,17 +155,14 @@ namespace osu.Game.Rulesets.Catch.Tests } } - private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + private class CatchCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { - private readonly bool hasColours; - - public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) - : base(createBeatmap(new CatchRuleset().RulesetInfo), null, null, audio) + public CatchCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) + : base(createBeatmap(new CatchRuleset().RulesetInfo), audio, hasColours) { - this.hasColours = hasColours; } - protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours); + protected override ISkin GetSkin() => new CatchTestBeatmapSkin(BeatmapInfo, HasColours); private static IBeatmap createBeatmap(RulesetInfo ruleset) { @@ -297,26 +226,19 @@ namespace osu.Game.Rulesets.Catch.Tests } } - private class TestBeatmapSkin : LegacyBeatmapSkin + private class CatchTestBeatmapSkin : TestBeatmapSkin { - public static Color4[] Colours { get; } = - { - new Color4(50, 100, 150, 255), - new Color4(40, 80, 120, 255), - }; - public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.DarkCyan; public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod; - public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) - : base(beatmap, new ResourceStore(), null) + public CatchTestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) + : base(beatmap, hasColours) { if (hasColours) { - Configuration.AddComboColours(Colours); Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); @@ -324,37 +246,24 @@ namespace osu.Game.Rulesets.Catch.Tests } } - private class TestSkin : LegacySkin, ISkinSource + private class CatchTestSkin : TestSkin { - public static Color4[] Colours { get; } = - { - new Color4(150, 100, 50, 255), - new Color4(20, 20, 20, 255), - }; - public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.LightCoral; public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan; - public TestSkin(bool hasCustomColours) - : base(new SkinInfo(), new ResourceStore(), null, string.Empty) + public CatchTestSkin(bool hasCustomColours) + : base(hasCustomColours) { if (hasCustomColours) { - Configuration.AddComboColours(Colours); Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); } } - - public event Action SourceChanged - { - add { } - remove { } - } } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 22b028906f..095ce63ec5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -1,168 +1,113 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Bindables; -using osu.Framework.IO.Stores; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Play; using osu.Game.Skinning; -using osu.Game.Tests.Visual; +using osu.Game.Tests.Beatmaps; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Tests { - public class TestSceneLegacyBeatmapSkin : ScreenTestScene + public class TestSceneLegacyBeatmapSkin : LegacyBeatmapSkinColourTest { [Resolved] private AudioManager audio { get; set; } - private readonly Bindable beatmapSkins = new Bindable(); - private readonly Bindable beatmapColours = new Bindable(); - [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - config.BindWith(OsuSetting.BeatmapSkins, beatmapSkins); - config.BindWith(OsuSetting.BeatmapColours, beatmapColours); + config.BindWith(OsuSetting.BeatmapSkins, BeatmapSkins); + config.BindWith(OsuSetting.BeatmapColours, BeatmapColours); } [TestCase(true, true)] [TestCase(true, false)] [TestCase(false, true)] [TestCase(false, false)] - public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) + public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, true); - AddStep("load coloured beatmap", () => player = loadBeatmap(userHasCustomColours, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin); + AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public void TestBeatmapComboColoursOverride(bool useBeatmapSkin) + public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, false); - AddStep("load coloured beatmap", () => player = loadBeatmap(true, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is user custom skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColoursOverride(useBeatmapSkin); + AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } [TestCase(true)] [TestCase(false)] - public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) + public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, false); - AddStep("load coloured beatmap", () => player = loadBeatmap(false, true)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true); + base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin); + AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } [TestCase(true, true)] [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) + public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, useBeatmapColour); - AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour); + AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours)); } [TestCase(true, true)] [TestCase(false, true)] [TestCase(true, false)] [TestCase(false, false)] - public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) + public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) { - ExposedPlayer player = null; - - configureSettings(useBeatmapSkin, useBeatmapColour); - AddStep("load custom-skin colour", () => player = loadBeatmap(true, false)); - AddUntilStep("wait for player", () => player.IsLoaded); - - AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours)); + TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false); + base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour); + AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } - private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours) + protected override ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new OsuExposedPlayer(userHasCustomColours); + + private class OsuExposedPlayer : ExposedPlayer { - ExposedPlayer player; - - Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours); - - LoadScreen(player = new ExposedPlayer(userHasCustomColours)); - - return player; - } - - private void configureSettings(bool beatmapSkins, bool beatmapColours) - { - AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () => + public OsuExposedPlayer(bool userHasCustomColours) + : base(userHasCustomColours) { - this.beatmapSkins.Value = beatmapSkins; - }); - AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () => - { - this.beatmapColours.Value = beatmapColours; - }); - } - - private class ExposedPlayer : Player - { - private readonly bool userHasCustomColours; - - public ExposedPlayer(bool userHasCustomColours) - : base(new PlayerConfiguration - { - AllowPause = false, - ShowResults = false, - }) - { - this.userHasCustomColours = userHasCustomColours; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(new TestSkin(userHasCustomColours)); + dependencies.CacheAs(new OsuTestSkin(UserHasCustomColours)); return dependencies; } - - public IReadOnlyList UsableComboColours => - GameplayClockContainer.ChildrenOfType() - .First() - .GetConfig>(GlobalSkinColours.ComboColours)?.Value; } - private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + private class OsuCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { private readonly bool hasColours; - public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) - : base(new Beatmap + public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) + : base(createBeatmap(), audio, hasColours) + { + this.hasColours = hasColours; + } + + protected override ISkin GetSkin() => new OsuTestBeatmapSkin(BeatmapInfo, hasColours); + + private static IBeatmap createBeatmap() => + new Beatmap { BeatmapInfo = { @@ -170,49 +115,22 @@ namespace osu.Game.Rulesets.Osu.Tests Ruleset = new OsuRuleset().RulesetInfo, }, HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } - }, null, null, audio) - { - this.hasColours = hasColours; - } - - protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours); + }; } - private class TestBeatmapSkin : LegacyBeatmapSkin + private class OsuTestBeatmapSkin : TestBeatmapSkin { - public static Color4[] Colours { get; } = + public OsuTestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) + : base(beatmap, hasColours) { - new Color4(50, 100, 150, 255), - new Color4(40, 80, 120, 255), - }; - - public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) - : base(beatmap, new ResourceStore(), null) - { - if (hasColours) - Configuration.AddComboColours(Colours); } } - private class TestSkin : LegacySkin, ISkinSource + private class OsuTestSkin : TestSkin { - public static Color4[] Colours { get; } = + public OsuTestSkin(bool hasCustomColours) + : base(hasCustomColours) { - new Color4(150, 100, 50, 255), - new Color4(20, 20, 20, 255), - }; - - public TestSkin(bool hasCustomColours) - : base(new SkinInfo(), null, null, string.Empty) - { - if (hasCustomColours) - Configuration.AddComboColours(Colours); - } - - public event Action SourceChanged - { - add { } - remove { } } } } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs new file mode 100644 index 0000000000..b42c3ea70d --- /dev/null +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -0,0 +1,147 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Bindables; +using osu.Framework.IO.Stores; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Screens.Play; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK.Graphics; + +namespace osu.Game.Tests.Beatmaps +{ + public class LegacyBeatmapSkinColourTest : ScreenTestScene + { + protected readonly Bindable BeatmapSkins = new Bindable(); + protected readonly Bindable BeatmapColours = new Bindable(); + protected ExposedPlayer TestPlayer; + protected WorkingBeatmap TestBeatmap; + + public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours); + + public virtual void TestBeatmapComboColoursOverride(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, true); + + public virtual void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, false); + + public virtual void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, false); + + public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true); + + protected virtual void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours) + { + configureSettings(useBeatmapSkin, useBeatmapColours); + AddStep($"load {(((CustomSkinWorkingBeatmap)TestBeatmap).HasColours ? "coloured " : "")} beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours)); + AddUntilStep("wait for player load", () => TestPlayer.IsLoaded); + } + + private void configureSettings(bool beatmapSkins, bool beatmapColours) + { + AddStep($"{(beatmapSkins ? "enable" : "disable")} beatmap skins", () => + { + BeatmapSkins.Value = beatmapSkins; + }); + AddStep($"{(beatmapColours ? "enable" : "disable")} beatmap colours", () => + { + BeatmapColours.Value = beatmapColours; + }); + } + + protected virtual ExposedPlayer LoadBeatmap(bool userHasCustomColours) + { + ExposedPlayer player; + + Beatmap.Value = TestBeatmap; + + LoadScreen(player = CreateTestPlayer(userHasCustomColours)); + + return player; + } + + protected virtual ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new ExposedPlayer(userHasCustomColours); + + protected class ExposedPlayer : Player + { + protected readonly bool UserHasCustomColours; + + public ExposedPlayer(bool userHasCustomColours) + : base(new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) + { + UserHasCustomColours = userHasCustomColours; + } + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(new TestSkin(UserHasCustomColours)); + return dependencies; + } + + public IReadOnlyList UsableComboColours => + GameplayClockContainer.ChildrenOfType() + .First() + .GetConfig>(GlobalSkinColours.ComboColours)?.Value; + } + + protected class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + public readonly bool HasColours; + + public CustomSkinWorkingBeatmap(IBeatmap beatmap, AudioManager audio, bool hasColours) + : base(beatmap, null, null, audio) + { + HasColours = hasColours; + } + + protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, HasColours); + } + + protected class TestBeatmapSkin : LegacyBeatmapSkin + { + public static Color4[] Colours { get; } = + { + new Color4(50, 100, 150, 255), + new Color4(40, 80, 120, 255), + }; + + public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) + : base(beatmap, new ResourceStore(), null) + { + if (hasColours) + Configuration.AddComboColours(Colours); + } + } + + protected class TestSkin : LegacySkin, ISkinSource + { + public static Color4[] Colours { get; } = + { + new Color4(150, 100, 50, 255), + new Color4(20, 20, 20, 255), + }; + + public TestSkin(bool hasCustomColours) + : base(new SkinInfo(), new ResourceStore(), null, string.Empty) + { + if (hasCustomColours) + Configuration.AddComboColours(Colours); + } + + public event Action SourceChanged + { + add { } + remove { } + } + } + } +} From a3535f4b79a3634e31fe9fd935545b653558787f Mon Sep 17 00:00:00 2001 From: Mysfit Date: Sat, 16 Jan 2021 02:09:35 -0500 Subject: [PATCH 069/385] Further simplified beatmap colouring tests. --- .../TestSceneLegacyBeatmapSkin.cs | 144 ++---------------- .../TestSceneLegacyBeatmapSkin.cs | 38 ----- .../Beatmaps/LegacyBeatmapSkinColourTest.cs | 22 +++ 3 files changed, 35 insertions(+), 169 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs index 89298242e5..eea83ef7c1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneLegacyBeatmapSkin.cs @@ -1,22 +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; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Testing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Skinning; -using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects; using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests @@ -90,9 +85,9 @@ namespace osu.Game.Rulesets.Catch.Tests { TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); ConfigureTest(useBeatmapSkin, true, true); - AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == CatchTestBeatmapSkin.HYPER_DASH_COLOUR); - AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == CatchTestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); - AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == CatchTestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR); + AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == TestBeatmapSkin.HYPER_DASH_FRUIT_COLOUR); } [TestCase(true)] @@ -101,9 +96,9 @@ namespace osu.Game.Rulesets.Catch.Tests { TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true); ConfigureTest(useBeatmapSkin, false, true); - AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == CatchTestSkin.HYPER_DASH_COLOUR); - AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == CatchTestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); - AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == CatchTestSkin.HYPER_DASH_FRUIT_COLOUR); + AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR); + AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR); + AddAssert("is custom hyper dash fruit colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashFruitColour == TestSkin.HYPER_DASH_FRUIT_COLOUR); } protected override ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new CatchExposedPlayer(userHasCustomColours); @@ -115,13 +110,6 @@ namespace osu.Game.Rulesets.Catch.Tests { } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(new CatchTestSkin(UserHasCustomColours)); - return dependencies; - } - public Color4 UsableHyperDashColour => GameplayClockContainer.ChildrenOfType() .First() @@ -141,129 +129,23 @@ namespace osu.Game.Rulesets.Catch.Tests .Value ?? Color4.Red; } - private class TestJuiceStream : JuiceStream - { - public TestJuiceStream(float x) - { - X = x; - - Path = new SliderPath(new[] - { - new PathControlPoint(Vector2.Zero), - new PathControlPoint(new Vector2(30, 0)), - }); - } - } - private class CatchCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { public CatchCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) - : base(createBeatmap(new CatchRuleset().RulesetInfo), audio, hasColours) + : base(createBeatmap(), audio, hasColours) { } - protected override ISkin GetSkin() => new CatchTestBeatmapSkin(BeatmapInfo, HasColours); - - private static IBeatmap createBeatmap(RulesetInfo ruleset) - { - var beatmap = new Beatmap + private static IBeatmap createBeatmap() => + new Beatmap { BeatmapInfo = { - Ruleset = ruleset, - BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f } - } + BeatmapSet = new BeatmapSetInfo(), + Ruleset = new CatchRuleset().RulesetInfo + }, + HitObjects = { new Fruit { StartTime = 1816, X = 56, NewCombo = true } } }; - - beatmap.ControlPointInfo.Add(0, new TimingControlPoint()); - - // Should produce a hyper-dash (edge case test) - beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 56, NewCombo = true }); - beatmap.HitObjects.Add(new Fruit { StartTime = 2008, X = 308, NewCombo = true }); - - double startTime = 3000; - - const float left_x = 0.02f * CatchPlayfield.WIDTH; - const float right_x = 0.98f * CatchPlayfield.WIDTH; - - createObjects(() => new Fruit { X = left_x }); - createObjects(() => new TestJuiceStream(right_x), 1); - createObjects(() => new TestJuiceStream(left_x), 1); - createObjects(() => new Fruit { X = right_x }); - createObjects(() => new Fruit { X = left_x }); - createObjects(() => new Fruit { X = right_x }); - createObjects(() => new TestJuiceStream(left_x), 1); - - beatmap.ControlPointInfo.Add(startTime, new TimingControlPoint - { - BeatLength = 50 - }); - - createObjects(() => new TestJuiceStream(left_x) - { - Path = new SliderPath(new[] - { - new PathControlPoint(Vector2.Zero), - new PathControlPoint(new Vector2(512, 0)) - }) - }, 1); - - return beatmap; - - void createObjects(Func createObject, int count = 3) - { - const float spacing = 140; - - for (int i = 0; i < count; i++) - { - var hitObject = createObject(); - hitObject.StartTime = startTime + i * spacing; - beatmap.HitObjects.Add(hitObject); - } - - startTime += 700; - } - } - } - - private class CatchTestBeatmapSkin : TestBeatmapSkin - { - public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; - - public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.DarkCyan; - - public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod; - - public CatchTestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) - : base(beatmap, hasColours) - { - if (hasColours) - { - Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); - Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); - Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); - } - } - } - - private class CatchTestSkin : TestSkin - { - public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; - - public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.LightCoral; - - public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan; - - public CatchTestSkin(bool hasCustomColours) - : base(hasCustomColours) - { - if (hasCustomColours) - { - Configuration.CustomColours.Add(CatchSkinColour.HyperDash.ToString(), HYPER_DASH_COLOUR); - Configuration.CustomColours.Add(CatchSkinColour.HyperDashAfterImage.ToString(), HYPER_DASH_AFTER_IMAGE_COLOUR); - Configuration.CustomColours.Add(CatchSkinColour.HyperDashFruit.ToString(), HYPER_DASH_FRUIT_COLOUR); - } - } } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs index 095ce63ec5..c26419b0e8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyBeatmapSkin.cs @@ -77,35 +77,13 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours)); } - protected override ExposedPlayer CreateTestPlayer(bool userHasCustomColours) => new OsuExposedPlayer(userHasCustomColours); - - private class OsuExposedPlayer : ExposedPlayer - { - public OsuExposedPlayer(bool userHasCustomColours) - : base(userHasCustomColours) - { - } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(new OsuTestSkin(UserHasCustomColours)); - return dependencies; - } - } - private class OsuCustomSkinWorkingBeatmap : CustomSkinWorkingBeatmap { - private readonly bool hasColours; - public OsuCustomSkinWorkingBeatmap(AudioManager audio, bool hasColours) : base(createBeatmap(), audio, hasColours) { - this.hasColours = hasColours; } - protected override ISkin GetSkin() => new OsuTestBeatmapSkin(BeatmapInfo, hasColours); - private static IBeatmap createBeatmap() => new Beatmap { @@ -117,21 +95,5 @@ namespace osu.Game.Rulesets.Osu.Tests HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } }; } - - private class OsuTestBeatmapSkin : TestBeatmapSkin - { - public OsuTestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) - : base(beatmap, hasColours) - { - } - } - - private class OsuTestSkin : TestSkin - { - public OsuTestSkin(bool hasCustomColours) - : base(hasCustomColours) - { - } - } } } diff --git a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs index b42c3ea70d..fb3432fbae 100644 --- a/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyBeatmapSkinColourTest.cs @@ -114,11 +114,22 @@ namespace osu.Game.Tests.Beatmaps new Color4(40, 80, 120, 255), }; + public static readonly Color4 HYPER_DASH_COLOUR = Color4.DarkBlue; + + public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.DarkCyan; + + public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.DarkGoldenrod; + public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours) : base(beatmap, new ResourceStore(), null) { if (hasColours) + { Configuration.AddComboColours(Colours); + Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR); + Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR); + Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR); + } } } @@ -130,11 +141,22 @@ namespace osu.Game.Tests.Beatmaps new Color4(20, 20, 20, 255), }; + public static readonly Color4 HYPER_DASH_COLOUR = Color4.LightBlue; + + public static readonly Color4 HYPER_DASH_AFTER_IMAGE_COLOUR = Color4.LightCoral; + + public static readonly Color4 HYPER_DASH_FRUIT_COLOUR = Color4.LightCyan; + public TestSkin(bool hasCustomColours) : base(new SkinInfo(), new ResourceStore(), null, string.Empty) { if (hasCustomColours) + { Configuration.AddComboColours(Colours); + Configuration.CustomColours.Add("HyperDash", HYPER_DASH_COLOUR); + Configuration.CustomColours.Add("HyperDashAfterImage", HYPER_DASH_AFTER_IMAGE_COLOUR); + Configuration.CustomColours.Add("HyperDashFruit", HYPER_DASH_FRUIT_COLOUR); + } } public event Action SourceChanged From bb0d2899931bbefedf3229d2b963a5d368fc6177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 16 Jan 2021 23:24:28 +0100 Subject: [PATCH 070/385] Split variable for readability --- osu.Game/OsuGameBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d3273ba170..716796a585 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -426,7 +426,8 @@ namespace osu.Game { var imports = tasks.Where(t => t.Path.EndsWith(ext, StringComparison.OrdinalIgnoreCase)); - return fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(ext))?.Import(imports.ToArray()) ?? Task.CompletedTask; + var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(ext)); + return importer?.Import(imports.ToArray()) ?? Task.CompletedTask; })); } From dee46d7ba201e70c463f68f5b4cf38c43a87d085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 16 Jan 2021 23:42:28 +0100 Subject: [PATCH 071/385] Use GroupBy() instead --- osu.Game/OsuGameBase.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 716796a585..1f8ae54e55 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -421,13 +421,11 @@ namespace osu.Game public virtual async Task Import(params ImportTask[] tasks) { - var extensions = tasks.Select(t => Path.GetExtension(t.Path).ToLowerInvariant()).Distinct(); - await Task.WhenAll(extensions.Select(ext => + var tasksPerExtension = tasks.GroupBy(t => Path.GetExtension(t.Path).ToLowerInvariant()); + await Task.WhenAll(tasksPerExtension.Select(taskGroup => { - var imports = tasks.Where(t => t.Path.EndsWith(ext, StringComparison.OrdinalIgnoreCase)); - - var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(ext)); - return importer?.Import(imports.ToArray()) ?? Task.CompletedTask; + var importer = fileImporters.FirstOrDefault(i => i.HandledExtensions.Contains(taskGroup.Key)); + return importer?.Import(taskGroup.ToArray()) ?? Task.CompletedTask; })); } From 816cc7a59b146cff51773d9f3ec862e0d61af7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Jan 2021 00:35:23 +0100 Subject: [PATCH 072/385] Adjust explicit label spacing on beatmap set overlay --- osu.Game/Overlays/BeatmapSet/Header.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 9f2b40dfa4..a84ed5ac29 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -145,14 +145,14 @@ namespace osu.Game.Overlays.BeatmapSet { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 3, Bottom = 4 }, // To better lineup with the font + Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font }, explicitPill = new ExplicitBeatmapPill { Alpha = 0f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 15f, Top = 4 }, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10, Bottom = 4 }, } } }, From eb53e32792dcb36c1aac9d612dc24f09733487dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Jan 2021 14:40:22 +0100 Subject: [PATCH 073/385] Use task completion source for room join flow On Android, users were unable to join or create multiplayer rooms. The root cause of that was that the both the wait and set of the `ManualResetEvent` in `getRoomUsers` occurred on the same thread, which created a chicken-and-egg situation - the set could not proceed until the wait had actually completed. Resolve by substituting the `ManualResetEvent` for a `TaskCompletionSource` to achieve a promise-style task, which the previous code was a crude approximation of anyway. Closes #11385. --- .../Multiplayer/StatefulMultiplayerClient.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index fbdfb6a8c5..f0e11b2b8b 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -128,7 +127,8 @@ namespace osu.Game.Online.Multiplayer Debug.Assert(Room != null); - var users = getRoomUsers(); + var users = await getRoomUsers(); + Debug.Assert(users != null); await Task.WhenAll(users.Select(PopulateUser)); @@ -437,24 +437,20 @@ namespace osu.Game.Online.Multiplayer /// This should be used whenever accessing users from outside of an Update thread context (ie. when not calling ). /// /// A copy of users in the current room, or null if unavailable. - private List? getRoomUsers() + private Task?> getRoomUsers() { - List? users = null; - - ManualResetEventSlim resetEvent = new ManualResetEventSlim(); + var tcs = new TaskCompletionSource?>(); // at some point we probably want to replace all these schedule calls with Room.LockForUpdate. // for now, as this would require quite some consideration due to the number of accesses to the room instance, // let's just add a manual schedule for the non-scheduled usages instead. Scheduler.Add(() => { - users = Room?.Users.ToList(); - resetEvent.Set(); + var users = Room?.Users.ToList(); + tcs.SetResult(users); }, false); - resetEvent.Wait(100); - - return users; + return tcs.Task; } /// From 5fd644fc576de33cab2f4b0daeb323b262b3b98e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Jan 2021 22:40:24 +0900 Subject: [PATCH 074/385] Unify variable names --- .../TestSceneBeatmapListingSearchControl.cs | 10 +++++----- osu.Game/Configuration/OsuConfigManager.cs | 4 ++-- .../Online/API/Requests/SearchBeatmapSetsRequest.cs | 8 ++++---- .../BeatmapListing/BeatmapListingFilterControl.cs | 4 ++-- .../BeatmapListing/BeatmapListingSearchControl.cs | 10 +++++----- .../Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs | 2 +- .../Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs | 2 +- ...citBeatmapPill.cs => ExplicitContentBeatmapPill.cs} | 4 ++-- osu.Game/Overlays/BeatmapSet/Header.cs | 6 +++--- .../Overlays/Settings/Sections/Online/WebSettings.cs | 2 +- .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 6 +++--- 11 files changed, 29 insertions(+), 29 deletions(-) rename osu.Game/Overlays/BeatmapSet/{ExplicitBeatmapPill.cs => ExplicitContentBeatmapPill.cs} (92%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index dc46da6293..a9747e73f9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.UserInterface control.Extra.BindCollectionChanged((u, v) => extra.Text = $"Extra: {(control.Extra.Any() ? string.Join('.', control.Extra.Select(i => i.ToString().ToLowerInvariant())) : "")}", true); control.Ranks.BindCollectionChanged((u, v) => ranks.Text = $"Ranks: {(control.Ranks.Any() ? string.Join('.', control.Ranks.Select(i => i.ToString())) : "")}", true); control.Played.BindValueChanged(p => played.Text = $"Played: {p.NewValue}", true); - control.Explicit.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true); + control.ExplicitContent.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true); }); [Test] @@ -92,11 +92,11 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestExplicitConfig() { - AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.AllowExplicitContent, true)); - AddAssert("explicit control set to show", () => control.Explicit.Value == SearchExplicit.Show); + AddStep("configure explicit content to allowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, true)); + AddAssert("explicit control set to show", () => control.ExplicitContent.Value == SearchExplicit.Show); - AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.AllowExplicitContent, false)); - AddAssert("explicit control set to hide", () => control.Explicit.Value == SearchExplicit.Hide); + AddStep("configure explicit content to disallowed", () => localConfig.Set(OsuSetting.ShowOnlineExplicitContent, false)); + AddAssert("explicit control set to hide", () => control.ExplicitContent.Value == SearchExplicit.Hide); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 5e7a843baf..6b48501dac 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -60,7 +60,7 @@ namespace osu.Game.Configuration Set(OsuSetting.ExternalLinkWarning, true); Set(OsuSetting.PreferNoVideo, false); - Set(OsuSetting.AllowExplicitContent, false); + Set(OsuSetting.ShowOnlineExplicitContent, false); // Audio Set(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); @@ -272,6 +272,6 @@ namespace osu.Game.Configuration EditorWaveformOpacity, DiscordRichPresence, AutomaticallyDownloadWhenSpectating, - AllowExplicitContent, + ShowOnlineExplicitContent, } } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 939d3c6cb4..5360d36f3d 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.API.Requests public SearchPlayed Played { get; } - public SearchExplicit Explicit { get; } + public SearchExplicit ExplicitContent { get; } [CanBeNull] public IReadOnlyCollection Ranks { get; } @@ -53,7 +53,7 @@ namespace osu.Game.Online.API.Requests IReadOnlyCollection extra = null, IReadOnlyCollection ranks = null, SearchPlayed played = SearchPlayed.Any, - SearchExplicit explicitMaps = SearchExplicit.Hide) + SearchExplicit explicitContent = SearchExplicit.Hide) { this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query); this.ruleset = ruleset; @@ -67,7 +67,7 @@ namespace osu.Game.Online.API.Requests Extra = extra; Ranks = ranks; Played = played; - Explicit = explicitMaps; + ExplicitContent = explicitContent; } protected override WebRequest CreateWebRequest() @@ -97,7 +97,7 @@ namespace osu.Game.Online.API.Requests if (Played != SearchPlayed.Any) req.AddParameter("played", Played.ToString().ToLowerInvariant()); - req.AddParameter("nsfw", Explicit == SearchExplicit.Show ? "true" : "false"); + req.AddParameter("nsfw", ExplicitContent == SearchExplicit.Show ? "true" : "false"); req.AddCursor(cursor); diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 650adcb4a9..bcc5a91677 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Extra.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Ranks.CollectionChanged += (_, __) => queueUpdateSearch(); searchControl.Played.BindValueChanged(_ => queueUpdateSearch()); - searchControl.Explicit.BindValueChanged(_ => queueUpdateSearch()); + searchControl.ExplicitContent.BindValueChanged(_ => queueUpdateSearch()); sortCriteria.BindValueChanged(_ => queueUpdateSearch()); sortDirection.BindValueChanged(_ => queueUpdateSearch()); @@ -195,7 +195,7 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Extra, searchControl.Ranks, searchControl.Played.Value, - searchControl.Explicit.Value); + searchControl.ExplicitContent.Value); getSetsRequest.Success += response => { diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 5c6267b726..b138a5ac52 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable Played => playedFilter.Current; - public Bindable Explicit => explicitFilter.Current; + public Bindable ExplicitContent => explicitContentFilter.Current; public BeatmapSetInfo BeatmapSet { @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.BeatmapListing private readonly BeatmapSearchMultipleSelectionFilterRow extraFilter; private readonly BeatmapSearchScoreFilterRow ranksFilter; private readonly BeatmapSearchFilterRow playedFilter; - private readonly BeatmapSearchFilterRow explicitFilter; + private readonly BeatmapSearchFilterRow explicitContentFilter; private readonly Box background; private readonly UpdateableBeatmapSetCover beatmapCover; @@ -130,7 +130,7 @@ namespace osu.Game.Overlays.BeatmapListing extraFilter = new BeatmapSearchMultipleSelectionFilterRow(@"Extra"), ranksFilter = new BeatmapSearchScoreFilterRow(), playedFilter = new BeatmapSearchFilterRow(@"Played"), - explicitFilter = new BeatmapSearchFilterRow(@"Explicit Maps"), + explicitContentFilter = new BeatmapSearchFilterRow(@"Explicit Content"), } } } @@ -148,10 +148,10 @@ namespace osu.Game.Overlays.BeatmapListing { background.Colour = colourProvider.Dark6; - allowExplicitContent = config.GetBindable(OsuSetting.AllowExplicitContent); + allowExplicitContent = config.GetBindable(OsuSetting.ShowOnlineExplicitContent); allowExplicitContent.BindValueChanged(allow => { - Explicit.Value = allow.NewValue ? SearchExplicit.Show : SearchExplicit.Hide; + ExplicitContent.Value = allow.NewValue ? SearchExplicit.Show : SearchExplicit.Hide; }, true); } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index b7002a96e5..c1d366bb82 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -205,7 +205,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) { - titleContainer.Add(new ExplicitBeatmapPill + titleContainer.Add(new ExplicitContentBeatmapPill { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 69671ab75b..76a30d1c11 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -219,7 +219,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) { - titleContainer.Add(new ExplicitBeatmapPill + titleContainer.Add(new ExplicitContentBeatmapPill { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs similarity index 92% rename from osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs rename to osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs index aefb3299a5..329f8ee0a2 100644 --- a/osu.Game/Overlays/BeatmapSet/ExplicitBeatmapPill.cs +++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs @@ -10,9 +10,9 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.BeatmapSet { - public class ExplicitBeatmapPill : CompositeDrawable + public class ExplicitContentBeatmapPill : CompositeDrawable { - public ExplicitBeatmapPill() + public ExplicitContentBeatmapPill() { AutoSizeAxes = Axes.Both; } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 876a7e8917..916c21c010 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box coverGradient; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; - private readonly ExplicitBeatmapPill explicitPill; + private readonly ExplicitContentBeatmapPill explicitContentPill; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; @@ -147,7 +147,7 @@ namespace osu.Game.Overlays.BeatmapSet Origin = Anchor.BottomLeft, Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font }, - explicitPill = new ExplicitBeatmapPill + explicitContentPill = new ExplicitContentBeatmapPill { Alpha = 0f, Anchor = Anchor.BottomLeft, @@ -261,7 +261,7 @@ namespace osu.Game.Overlays.BeatmapSet title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; - explicitPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index da7ef46f65..3e1cc1d91e 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online { LabelText = "Hide warnings for explicit content in beatmaps", Keywords = new[] { "nsfw", "18+", "offensive" }, - Current = config.GetBindable(OsuSetting.AllowExplicitContent), + Current = config.GetBindable(OsuSetting.ShowOnlineExplicitContent), } }; } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 7987d715e3..b16f82fce9 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay private Container difficultyIconContainer; private LinkFlowContainer beatmapText; private LinkFlowContainer authorText; - private ExplicitBeatmapPill explicitPill; + private ExplicitContentBeatmapPill explicitContentPill; private ModDisplay modDisplay; private readonly Bindable beatmap = new Bindable(); @@ -119,7 +119,7 @@ namespace osu.Game.Screens.OnlinePlay } bool hasExplicitContent = Item.Beatmap.Value.BeatmapSet.OnlineInfo?.HasExplicitContent == true; - explicitPill.Alpha = hasExplicitContent ? 1 : 0; + explicitContentPill.Alpha = hasExplicitContent ? 1 : 0; modDisplay.Current.Value = requiredMods.ToArray(); } @@ -181,7 +181,7 @@ namespace osu.Game.Screens.OnlinePlay Children = new Drawable[] { authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, - explicitPill = new ExplicitBeatmapPill + explicitContentPill = new ExplicitContentBeatmapPill { Alpha = 0f, Anchor = Anchor.CentreLeft, From 5278cad393aeb1d76f7603cda445fb67b2d46234 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 17 Jan 2021 22:40:58 +0900 Subject: [PATCH 075/385] Reword setting to make more sense --- osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs index 3e1cc1d91e..59bcbe4d89 100644 --- a/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Online/WebSettings.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.Online }, new SettingsCheckbox { - LabelText = "Hide warnings for explicit content in beatmaps", + LabelText = "Show explicit content in search results", Keywords = new[] { "nsfw", "18+", "offensive" }, Current = config.GetBindable(OsuSetting.ShowOnlineExplicitContent), } From 39746cb3de8ad11221ff1766822f720918d49858 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 00:11:35 +0900 Subject: [PATCH 076/385] Test removing nuget restore from iOS/Android build scripts --- fastlane/Fastfile | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 8c278604aa..18b5907e82 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -48,10 +48,6 @@ desc 'Deploy to play store' desc 'Compile the project' lane :build do |options| - nuget_restore( - project_path: 'osu.sln' - ) - souyuz( build_configuration: 'Release', solution_path: 'osu.sln', @@ -107,10 +103,6 @@ platform :ios do desc 'Compile the project' lane :build do - nuget_restore( - project_path: 'osu.sln' - ) - souyuz( platform: "ios", plist_path: "osu.iOS/Info.plist" From 585aa87c5389b16dc9c8d4c17c38365cb0100174 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 17 Jan 2021 19:17:14 +0300 Subject: [PATCH 077/385] Fix playlist item download button never shown back after hiding --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 8 +++++++- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 17d85d1120..c6cfd3c64a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -201,11 +201,17 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestDownloadButtonHiddenInitiallyWhenBeatmapExists() + public void TestDownloadButtonHiddenWhenBeatmapExists() { createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); AddAssert("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); + + AddStep("delete beatmap set", () => manager.Delete(manager.QueryBeatmapSets(_ => true).Single())); + AddUntilStep("download button shown", () => playlist.ChildrenOfType().Single().IsPresent); + + AddStep("undelete beatmap set", () => manager.Undelete(manager.QueryBeatmapSets(_ => true).Single())); + AddUntilStep("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); } [Test] diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index b16f82fce9..f8982582d5 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -249,6 +249,8 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] private BeatmapManager beatmapManager { get; set; } + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public PlaylistDownloadButton(PlaylistItem playlistItem) : base(playlistItem.Beatmap.Value.BeatmapSet) { From 2b23c8eabd02a1c3743d7538bbecb9ae3d224333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Jan 2021 18:08:58 +0100 Subject: [PATCH 078/385] Use alpha directly for checking visibility in test `IsPresent` is no longer synonymous with being visible, after applying the fix to the issue in question. --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index c6cfd3c64a..874c1694eb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -205,13 +205,16 @@ namespace osu.Game.Tests.Visual.Multiplayer { createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); - AddAssert("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); + assertDownloadButtonVisible(false); AddStep("delete beatmap set", () => manager.Delete(manager.QueryBeatmapSets(_ => true).Single())); - AddUntilStep("download button shown", () => playlist.ChildrenOfType().Single().IsPresent); + assertDownloadButtonVisible(true); AddStep("undelete beatmap set", () => manager.Undelete(manager.QueryBeatmapSets(_ => true).Single())); - AddUntilStep("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); + assertDownloadButtonVisible(false); + + void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}", + () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); } [Test] From ec00aaef90a9bfe8d768f01148f99a78d043401e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Jan 2021 19:13:55 +0900 Subject: [PATCH 079/385] Add nuget deploys for all rulesets --- appveyor_deploy.yml | 73 +++++++++++++++---- .../osu.Game.Rulesets.Catch.csproj | 7 ++ .../osu.Game.Rulesets.Mania.csproj | 7 ++ .../osu.Game.Rulesets.Osu.csproj | 7 ++ .../osu.Game.Rulesets.Taiko.csproj | 7 ++ 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index bb4482f501..737e5c43ab 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -1,21 +1,68 @@ clone_depth: 1 version: '{build}' image: Visual Studio 2019 -dotnet_csproj: - patch: true - file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects - version: $(APPVEYOR_REPO_TAG_NAME) -before_build: - - ps: dotnet --info # Useful when version mismatch between CI and local - - ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects test: off skip_non_tags: true configuration: Release -build: - project: build\Desktop.proj # Skipping Xamarin Release that's slow and covered by fastlane - parallel: true - verbosity: minimal - publish_nuget: true + +environment: + matrix: + - job_name: osu-game + - job_name: osu-ruleset + job_depends_on: osu-game + - job_name: taiko-ruleset + job_depends_on: osu-game + - job_name: catch-ruleset + job_depends_on: osu-game + - job_name: mania-ruleset + job_depends_on: osu-game + +nuget: + project_feed: true + +for: + - + matrix: + only: + - job_name: osu-game + build_script: + - cmd: dotnet pack osu.Game\osu.Game.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME% + - + matrix: + only: + - job_name: osu-ruleset + build_script: + - cmd: dotnet remove osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj reference osu.Game\osu.Game.csproj + - cmd: dotnet add osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME% + - cmd: dotnet pack osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME% + - + matrix: + only: + - job_name: taiko-ruleset + build_script: + - cmd: dotnet remove osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj reference osu.Game\osu.Game.csproj + - cmd: dotnet add osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME% + - cmd: dotnet pack osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME% + - + matrix: + only: + - job_name: catch-ruleset + build_script: + - cmd: dotnet remove osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj reference osu.Game\osu.Game.csproj + - cmd: dotnet add osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME% + - cmd: dotnet pack osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME% + - + matrix: + only: + - job_name: mania-ruleset + build_script: + - cmd: dotnet remove osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj reference osu.Game\osu.Game.csproj + - cmd: dotnet add osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj package ppy.osu.Game -v %APPVEYOR_REPO_TAG_NAME% + - cmd: dotnet pack osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj /p:Version=%APPVEYOR_REPO_TAG_NAME% + +artifacts: + - path: '**\*.nupkg' + deploy: - provider: Environment - name: nuget + name: nuget \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b19affbf9f..5bdf39824c 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -5,6 +5,13 @@ true catch the fruit. to the beat. + + + osu!catch + ppy.osu.Game.Rulesets.Catch + true + + diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 07ef1022ae..c30fe8494f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -5,6 +5,13 @@ true smash the keys. to the beat. + + + osu!mania + ppy.osu.Game.Rulesets.Mania + true + + diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index bffeaabb55..36ee804259 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -5,6 +5,13 @@ true click the circles. to the beat. + + + osu!standard + ppy.osu.Game.Rulesets.Osu + true + + diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index ebed8c6d7c..00deb719e1 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -5,6 +5,13 @@ true bash the drum. to the beat. + + + osu!taiko + ppy.osu.Game.Rulesets.Taiko + true + + From 1b166d809eb5bb5f04bdbcfabfba3916cd66a114 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Jan 2021 11:07:54 +0900 Subject: [PATCH 080/385] Adjust package titles --- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 2 +- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 2 +- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 2 +- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 5bdf39824c..e2f95ca177 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -7,7 +7,7 @@ - osu!catch + osu!catch (ruleset) ppy.osu.Game.Rulesets.Catch true diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index c30fe8494f..4f6840f9ca 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -7,7 +7,7 @@ - osu!mania + osu!mania (ruleset) ppy.osu.Game.Rulesets.Mania true diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 36ee804259..98f1e69bd1 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -7,7 +7,7 @@ - osu!standard + osu! (ruleset) ppy.osu.Game.Rulesets.Osu true diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 00deb719e1..b752c13d18 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -7,7 +7,7 @@ - osu!taiko + osu!taiko (ruleset) ppy.osu.Game.Rulesets.Taiko true From 94fee8c31d123b940ddf636cf6982152479cc7ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 16:13:58 +0900 Subject: [PATCH 081/385] Avoid doing a config lookup if initial conditional fails --- osu.Game/Skinning/SkinProvidingContainer.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index e97822b86e..27cf0c697a 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -74,8 +74,8 @@ namespace osu.Game.Skinning { if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup) return lookupWithFallback(lookup, AllowColourLookup); - else - return lookupWithFallback(lookup, AllowConfigurationLookup); + + return lookupWithFallback(lookup, AllowConfigurationLookup); } return fallbackSource?.GetConfig(lookup); @@ -83,11 +83,14 @@ namespace osu.Game.Skinning private IBindable lookupWithFallback(TLookup lookup, bool canUseSkinLookup) { - var bindable = skin.GetConfig(lookup); - if (bindable != null && canUseSkinLookup) - return bindable; - else - return fallbackSource?.GetConfig(lookup); + if (canUseSkinLookup) + { + var bindable = skin.GetConfig(lookup); + if (bindable != null) + return bindable; + } + + return fallbackSource?.GetConfig(lookup); } protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); From 02d2b2742b66d1bf6b9899430d90580f69872db1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 16:57:36 +0900 Subject: [PATCH 082/385] Fix selection box not updating with hitcircles/sliders far in the future or past --- .../Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs | 2 +- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs index 093bae854e..abbb54e3c1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs @@ -30,6 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos); - public override Quad SelectionQuad => DrawableObject.HitArea.ScreenSpaceDrawQuad; + public override Quad SelectionQuad => CirclePiece.ScreenSpaceDrawQuad; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index d592e129d9..3d3dff653a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -44,6 +44,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private IEditorChangeHandler changeHandler { get; set; } + public override Quad SelectionQuad => BodyPiece.ScreenSpaceDrawQuad; + private readonly BindableList controlPoints = new BindableList(); private readonly IBindable pathVersion = new Bindable(); From c79ab63743e14b4205e40f33d5ae4519dfbeb58e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 16:56:10 +0900 Subject: [PATCH 083/385] Fix sliders with an even number of repeats not allowing rotation/scale transforms --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 660e1844aa..91bb665ee2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -236,7 +236,18 @@ namespace osu.Game.Rulesets.Osu.Edit /// /// The hit objects to calculate a quad for. private Quad getSurroundingQuad(OsuHitObject[] hitObjects) => - getSurroundingQuad(hitObjects.SelectMany(h => new[] { h.Position, h.EndPosition })); + getSurroundingQuad(hitObjects.SelectMany(h => + { + if (h is IHasPath path) + return new[] + { + h.Position, + // can't use EndPosition for reverse slider cases. + h.Position + path.Path.PositionAt(1) + }; + + return new[] { h.Position }; + })); /// /// Returns a gamefield-space quad surrounding the provided points. From 2f1d4bf51b0964aded4fa5d31be592c7da3eb482 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 17:13:47 +0900 Subject: [PATCH 084/385] Add missing braces --- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 91bb665ee2..871339ae7b 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -239,12 +239,14 @@ namespace osu.Game.Rulesets.Osu.Edit getSurroundingQuad(hitObjects.SelectMany(h => { if (h is IHasPath path) + { return new[] { h.Position, // can't use EndPosition for reverse slider cases. h.Position + path.Path.PositionAt(1) }; + } return new[] { h.Position }; })); From 0b165dce4bbbb0647ccbdae48b8d2621173655f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 17:50:32 +0900 Subject: [PATCH 085/385] Fix multiplayer mod select showing autoplay as a choice --- osu.Game/OsuGameBase.cs | 1 + osu.Game/Overlays/Mods/ModSelectOverlay.cs | 7 +++++-- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 5 +++++ osu.Game/Screens/Select/SongSelect.cs | 8 +++----- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1f8ae54e55..20d88d33f2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -327,6 +327,7 @@ namespace osu.Game if (!SelectedMods.Disabled) SelectedMods.Value = Array.Empty(); + AvailableMods.Value = dict; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 0c8245bebe..b93602116b 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -30,6 +30,7 @@ namespace osu.Game.Overlays.Mods { public class ModSelectOverlay : WaveOverlayContainer { + private readonly Func isValidMod; public const float HEIGHT = 510; protected readonly TriangleButton DeselectAllButton; @@ -60,8 +61,10 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - public ModSelectOverlay() + public ModSelectOverlay(Func isValidMod = null) { + this.isValidMod = isValidMod ?? (m => true); + Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); @@ -403,7 +406,7 @@ namespace osu.Game.Overlays.Mods if (mods.NewValue == null) return; foreach (var section in ModSectionsContainer.Children) - section.Mods = mods.NewValue[section.ModType]; + section.Mods = mods.NewValue[section.ModType].Where(isValidMod); } private void selectedModsChanged(ValueChangedEvent> mods) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 36dbb9e792..ebc06d2445 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; @@ -109,5 +110,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + + protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + + private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6c0bd3a228..4fca77a176 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -251,11 +251,7 @@ namespace osu.Game.Screens.Select Children = new Drawable[] { BeatmapOptions = new BeatmapOptionsOverlay(), - ModSelect = new ModSelectOverlay - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - } + ModSelect = CreateModSelectOverlay() } } } @@ -305,6 +301,8 @@ namespace osu.Game.Screens.Select } } + protected virtual ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(); + protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { // if not the current screen, we want to get carousel in a good presentation state before displaying (resume or enter). From 1359153382232fc303dd1ded30048545c4aa0638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 17:54:22 +0900 Subject: [PATCH 086/385] Revert "Test removing nuget restore from iOS/Android build scripts" This reverts commit 39746cb3de8ad11221ff1766822f720918d49858. --- fastlane/Fastfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 18b5907e82..8c278604aa 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -48,6 +48,10 @@ desc 'Deploy to play store' desc 'Compile the project' lane :build do |options| + nuget_restore( + project_path: 'osu.sln' + ) + souyuz( build_configuration: 'Release', solution_path: 'osu.sln', @@ -103,6 +107,10 @@ platform :ios do desc 'Compile the project' lane :build do + nuget_restore( + project_path: 'osu.sln' + ) + souyuz( platform: "ios", plist_path: "osu.iOS/Info.plist" From 46681322d0489d0829114fc87071b7e5c8f0c525 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 17:56:00 +0900 Subject: [PATCH 087/385] Restore nuget packages per project to avoid toolchain incompatibilities with net50 --- fastlane/Fastfile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 8c278604aa..1823b9e924 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -48,9 +48,8 @@ desc 'Deploy to play store' desc 'Compile the project' lane :build do |options| - nuget_restore( - project_path: 'osu.sln' - ) + nuget_restore(project_path: 'osu.Android/osu.Android.csproj') + nuget_restore(project_path: 'osu.Game/osu.Game.csproj') souyuz( build_configuration: 'Release', @@ -107,9 +106,8 @@ platform :ios do desc 'Compile the project' lane :build do - nuget_restore( - project_path: 'osu.sln' - ) + nuget_restore(project_path: 'osu.iOS/osu.iOS.csproj') + nuget_restore(project_path: 'osu.Game/osu.Game.csproj') souyuz( platform: "ios", From 0560eb41201d0094d8321927bc3498502065c994 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 18:22:21 +0900 Subject: [PATCH 088/385] Reduce final fill alpha of main menu confirm-to-exit --- osu.Game/Overlays/HoldToConfirmOverlay.cs | 13 +++++++++++-- osu.Game/Screens/Menu/ExitConfirmOverlay.cs | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index eb325d8dd3..0542f66b5b 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -24,6 +24,13 @@ namespace osu.Game.Overlays [Resolved] private AudioManager audio { get; set; } + private readonly float finalFillAlpha; + + protected HoldToConfirmOverlay(float finalFillAlpha = 1) + { + this.finalFillAlpha = finalFillAlpha; + } + [BackgroundDependencyLoader] private void load() { @@ -42,8 +49,10 @@ namespace osu.Game.Overlays Progress.ValueChanged += p => { - audioVolume.Value = 1 - p.NewValue; - overlay.Alpha = (float)p.NewValue; + var target = p.NewValue * finalFillAlpha; + + audioVolume.Value = 1 - target; + overlay.Alpha = (float)target; }; audio.Tracks.AddAdjustment(AdjustableProperty.Volume, audioVolume); diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index db2faeb60a..a491283e5f 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -13,6 +13,11 @@ namespace osu.Game.Screens.Menu public void Abort() => AbortConfirm(); + public ExitConfirmOverlay() + : base(0.7f) + { + } + public bool OnPressed(GlobalAction action) { if (action == GlobalAction.Back) From 12443e39ae70060657f1b097bc7b9c6e35345e08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 19:16:32 +0900 Subject: [PATCH 089/385] 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 db5c933c41..9ad5946311 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 301ee39a61..2b8f81532d 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 225cf981f2..4732620085 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From ced7a36788d7f61ce1163f043d872eb97ad44a02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 21:24:10 +0900 Subject: [PATCH 090/385] Update namespaces --- osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs | 1 - osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs | 2 +- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 1 - osu.Game/Rulesets/UI/Playfield.cs | 2 +- osu.Game/Skinning/PoolableSkinnableSample.cs | 2 +- osu.Game/Skinning/SkinnableSound.cs | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs index 33e3c7cb8c..987a5812db 100644 --- a/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs +++ b/osu.Game.Tests/Rulesets/TestSceneDrawableRulesetDependencies.cs @@ -9,7 +9,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 80f1b02794..97087e31ab 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; +using osu.Framework.Audio.Sample; using osu.Framework.Configuration.Tracking; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index b13b20dae2..81ec73a6c5 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index b4e0025351..c40ab4bd94 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -8,7 +8,6 @@ using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Containers; @@ -20,6 +19,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Skinning; using osuTK; using System.Diagnostics; +using osu.Framework.Audio.Sample; namespace osu.Game.Rulesets.UI { diff --git a/osu.Game/Skinning/PoolableSkinnableSample.cs b/osu.Game/Skinning/PoolableSkinnableSample.cs index cc6b85a13e..2a0f480b48 100644 --- a/osu.Game/Skinning/PoolableSkinnableSample.cs +++ b/osu.Game/Skinning/PoolableSkinnableSample.cs @@ -5,7 +5,7 @@ using System; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Track; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index b841f99598..a874e9a0db 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -7,7 +7,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Audio.Track; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; From e74ecebfd61440a47c7423b0802491ccc1930b7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jan 2021 23:45:57 +0900 Subject: [PATCH 091/385] nuget restore other rulesets --- fastlane/Fastfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 1823b9e924..cc5abf5b03 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -50,6 +50,10 @@ desc 'Deploy to play store' lane :build do |options| nuget_restore(project_path: 'osu.Android/osu.Android.csproj') nuget_restore(project_path: 'osu.Game/osu.Game.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj') souyuz( build_configuration: 'Release', @@ -108,6 +112,10 @@ platform :ios do lane :build do nuget_restore(project_path: 'osu.iOS/osu.iOS.csproj') nuget_restore(project_path: 'osu.Game/osu.Game.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj') + nuget_restore(project_path: 'osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj') souyuz( platform: "ios", From ddcfd854bd1c66b673d73e5459526ded76ebcb41 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Jan 2021 19:20:19 +0300 Subject: [PATCH 092/385] Wait for scheduled state changes before asserting --- osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs index 4b8992052e..d692e6f9a3 100644 --- a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs +++ b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs @@ -130,6 +130,8 @@ namespace osu.Game.Tests.Online private void addAvailabilityCheckStep(string description, Func expected) { + // In DownloadTrackingComposite, state changes are scheduled one frame later, wait one step. + AddWaitStep("wait for potential change", 1); AddAssert(description, () => tracker.Availability.Value.Equals(expected.Invoke())); } From 25f511fd5b2b6b2bfbd9e1787182a8a87f086de0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Jan 2021 21:34:24 +0300 Subject: [PATCH 093/385] Remove unnecessary full querying --- osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs b/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs index 659f52f00f..c2c800badc 100644 --- a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs +++ b/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs @@ -48,17 +48,7 @@ namespace osu.Game.Online.Rooms int? beatmapId = SelectedItem.Value.Beatmap.Value.OnlineBeatmapID; string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; - BeatmapInfo matchingBeatmap; - - if (databasedSet.Beatmaps == null) - { - // The given databased beatmap set is not passed in a usable state to check with. - // Perform a full query instead, as per https://github.com/ppy/osu/pull/11415. - matchingBeatmap = Manager.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - return matchingBeatmap != null; - } - - matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); + var matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); return matchingBeatmap != null; } From 5e476fa189d7637db79c228721a601a005f2d355 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Jan 2021 22:07:25 +0300 Subject: [PATCH 094/385] Enforce one missed property back to single-floating type --- osu.Game/Online/API/ArchiveDownloadRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/ArchiveDownloadRequest.cs b/osu.Game/Online/API/ArchiveDownloadRequest.cs index ccb4e9c119..0bf238109e 100644 --- a/osu.Game/Online/API/ArchiveDownloadRequest.cs +++ b/osu.Game/Online/API/ArchiveDownloadRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API { public readonly TModel Model; - public double Progress { get; private set; } + public float Progress { get; private set; } public event Action DownloadProgressed; From 7476cb3047b400489d39af6494c782de531313ed Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 18 Jan 2021 19:51:42 +0000 Subject: [PATCH 095/385] Sort SkinSection in alphabetical order --- .../Overlays/Settings/Sections/SkinSection.cs | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 5898482e4a..0bfa0ba4f0 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -107,21 +107,20 @@ namespace osu.Game.Overlays.Settings.Sections private void updateItems() { skinItems = skins.GetAllUsableSkins(); - - // insert after lazer built-in skins - int firstNonDefault = skinItems.FindIndex(s => s.ID > 0); - if (firstNonDefault < 0) - firstNonDefault = skinItems.Count; - - skinItems.Insert(firstNonDefault, random_skin_info); - + skinItems = sortList(skinItems); + skinDropdown.Items = skinItems; } private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) - Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToArray()); + { + List newDropdownItems = skinDropdown.Items.ToList(); + newDropdownItems.Add(item); + newDropdownItems = sortList(newDropdownItems); + Schedule(() => skinDropdown.Items = newDropdownItems.ToArray()); + } } private void itemRemoved(ValueChangedEvent> weakItem) @@ -130,6 +129,24 @@ namespace osu.Game.Overlays.Settings.Sections Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != item.ID).ToArray()); } + private List sortList(List skinsList) + { + skinsList.Sort((a, b) => String.Compare(a.Name, b.Name, StringComparison.Ordinal)); + for (int i = 0; i < skinsList.Count; i++) + { + // insert lazer built-in skins before user skins + if (skinsList[i].ID <= 0) { + var itemToMove = skinsList[i]; + skinsList.RemoveAt(i); + skinsList.Insert(0, itemToMove); + } + } + skinsList.RemoveAll(s => s.ID == SkinInfo.RANDOM_SKIN); + skinsList.Insert(0, random_skin_info); + + return skinsList; + } + private class SkinSettingsDropdown : SettingsDropdown { protected override OsuDropdown CreateDropdown() => new SkinDropdownControl(); From 63ca9de7e4a85990bd162d25cd9be01f8c91c239 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Jan 2021 23:00:07 +0300 Subject: [PATCH 096/385] Rewerite beatmap term properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs b/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs index c2c800badc..32a6c39ea8 100644 --- a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs +++ b/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs @@ -38,7 +38,7 @@ namespace osu.Game.Online.Rooms { var verified = verifyDatabasedModel(databasedSet); if (!verified) - Logger.Log("The imported beatmapset does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); + Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); return verified; } From 0b65c0cd25a727fbfa121395e515fe9e8bd295b1 Mon Sep 17 00:00:00 2001 From: rednir Date: Mon, 18 Jan 2021 20:17:42 +0000 Subject: [PATCH 097/385] Remove whitespace --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 8e78940ac2..20953b7a9b 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -113,7 +113,6 @@ namespace osu.Game.Overlays.Settings.Sections { skinItems = skins.GetAllUsableSkins(); skinItems = sortList(skinItems); - skinDropdown.Items = skinItems; } From 5233a0449a63b3db599d11535b3484c8ebc50f47 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Mon, 18 Jan 2021 16:08:06 -0500 Subject: [PATCH 098/385] Hide main room subscreen on initial mp room creation. Toggle mp room subscreen visibility based on settings overlay visibility before room is created. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 80991569dc..df61a0ad21 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,6 +39,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; + private readonly Bindable settingsOverlayVisibility = new Bindable(); + + private GridContainer subScreenContainer; + private IBindable isConnected; [CanBeNull] @@ -55,7 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { InternalChildren = new Drawable[] { - new GridContainer + subScreenContainer = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] @@ -178,6 +182,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } } }; + + subScreenContainer.Hide(); + settingsOverlayVisibility.BindTo(settingsOverlay.State); + settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; } protected override void LoadComplete() @@ -258,6 +266,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } + private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) + { + if (client.Room != null) + { + subScreenContainer.Show(); + settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; + } + else + { + if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) + subScreenContainer.Hide(); + else + subScreenContainer.Show(); + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From cbfb999c2845074d799d1126296584898d9be52b Mon Sep 17 00:00:00 2001 From: Mysfit Date: Mon, 18 Jan 2021 17:13:24 -0500 Subject: [PATCH 099/385] Use the client.RoomUpdated action instead of binding the value of the settings overlay visibility and creating an event from it based on its ValueChanged action. --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index df61a0ad21..38c2ca7e0c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -39,8 +39,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private MultiplayerMatchSettingsOverlay settingsOverlay; - private readonly Bindable settingsOverlayVisibility = new Bindable(); - private GridContainer subScreenContainer; private IBindable isConnected; @@ -184,8 +182,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }; subScreenContainer.Hide(); - settingsOverlayVisibility.BindTo(settingsOverlay.State); - settingsOverlayVisibility.ValueChanged += settingsOverlayVisibilityChanged; + client.RoomUpdated += roomUpdated; } protected override void LoadComplete() @@ -266,19 +263,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer readyClickOperation = null; } - private void settingsOverlayVisibilityChanged(ValueChangedEvent settingsOverlayVisibilityChangedEvent) + private void roomUpdated() { if (client.Room != null) { + // If the room is updated and is not null, show the room sub screen container and unsubscribe. subScreenContainer.Show(); - settingsOverlayVisibility.ValueChanged -= settingsOverlayVisibilityChanged; - } - else - { - if (settingsOverlayVisibilityChangedEvent.NewValue == Visibility.Visible) - subScreenContainer.Hide(); - else - subScreenContainer.Show(); + client.RoomUpdated -= roomUpdated; } } From f0add0a7cff7b6fe63ca0ccbf0de4caa130fd30d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 19 Jan 2021 01:34:37 +0300 Subject: [PATCH 100/385] Make BeatmapSetOverlay use OverlayHeader --- .../Online/TestSceneBeatmapSetOverlay.cs | 2 +- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 300 ++++++++++++++++- osu.Game/Overlays/BeatmapSet/Header.cs | 313 ------------------ osu.Game/Overlays/BeatmapSetOverlay.cs | 9 +- 4 files changed, 302 insertions(+), 322 deletions(-) delete mode 100644 osu.Game/Overlays/BeatmapSet/Header.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 689321698a..7ff978c7ca 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -315,7 +315,7 @@ namespace osu.Game.Tests.Visual.Online private class TestBeatmapSetOverlay : BeatmapSetOverlay { - public new Header Header => base.Header; + public new BeatmapSetHeader Header => base.Header; } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 6511b15fc8..bc9008d1f5 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -1,23 +1,319 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapSetHeader : OverlayHeader { - public readonly Bindable Ruleset = new Bindable(); + private const float transition_duration = 200; + private const float buttons_height = 45; + private const float buttons_spacing = 5; + + public readonly Bindable BeatmapSet = new Bindable(); + + public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); + + public BeatmapPicker Picker { get; private set; } public BeatmapRulesetSelector RulesetSelector { get; private set; } + private IBindable state => downloadTracker.State; + + [Cached(typeof(IBindable))] + private readonly Bindable ruleset = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + private readonly DownloadTracker downloadTracker; + private OsuSpriteText title, artist; + private AuthorInfo author; + private ExplicitContentBeatmapPill explicitContentPill; + private FillFlowContainer downloadButtonsContainer; + private BeatmapAvailability beatmapAvailability; + private BeatmapSetOnlineStatusPill onlineStatusPill; + private ExternalLinkButton externalLink; + private UpdateableBeatmapSetCover cover; + private Box coverGradient; + private FillFlowContainer fadeContent; + private FavouriteButton favouriteButton; + private LoadingSpinner loading; + private Details details; + + public BeatmapSetHeader() + { + Masking = true; + + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.25f), + Type = EdgeEffectType.Shadow, + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + + AddInternal(downloadTracker = new DownloadTracker + { + BeatmapSet = { BindTarget = BeatmapSet } + }); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Picker.Beatmap.ValueChanged += b => + { + details.Beatmap = b.NewValue; + externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; + }; + + coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); + onlineStatusPill.BackgroundColour = colourProvider.Background6; + + state.BindValueChanged(_ => updateDownloadButtons()); + + BeatmapSet.BindValueChanged(setInfo => + { + Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = details.BeatmapSet = setInfo.NewValue; + cover.BeatmapSet = setInfo.NewValue; + + if (setInfo.NewValue == null) + { + onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); + fadeContent.Hide(); + + loading.Show(); + + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } + else + { + fadeContent.FadeIn(500, Easing.OutQuint); + + loading.Hide(); + + title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; + artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; + + explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + + onlineStatusPill.FadeIn(500, Easing.OutQuint); + onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; + + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + + updateDownloadButtons(); + } + }, true); + } + + protected override Drawable CreateContent() => new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + cover = new UpdateableBeatmapSetCover + { + RelativeSizeAxes = Axes.Both, + Masking = true, + }, + coverGradient = new Box + { + RelativeSizeAxes = Axes.Both + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = BeatmapSetOverlay.Y_PADDING, + Left = BeatmapSetOverlay.X_PADDING, + Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, + }, + Children = new Drawable[] + { + fadeContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = Picker = new BeatmapPicker(), + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 15 }, + Children = new Drawable[] + { + title = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) + }, + externalLink = new ExternalLinkButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font + }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10, Bottom = 4 }, + } + } + }, + artist = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), + Margin = new MarginPadding { Bottom = 20 } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = author = new AuthorInfo(), + }, + beatmapAvailability = new BeatmapAvailability(), + new Container + { + RelativeSizeAxes = Axes.X, + Height = buttons_height, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] + { + favouriteButton = new FavouriteButton + { + BeatmapSet = { BindTarget = BeatmapSet } + }, + downloadButtonsContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, + Spacing = new Vector2(buttons_spacing), + }, + }, + }, + }, + }, + } + }, + loading = new LoadingSpinner + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + }, + new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + Children = new Drawable[] + { + onlineStatusPill = new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 14, + TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } + }, + details = new Details(), + }, + }, + } + }; + + private void updateDownloadButtons() + { + if (BeatmapSet.Value == null) return; + + if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && state.Value != DownloadState.LocallyAvailable) + { + downloadButtonsContainer.Clear(); + return; + } + + switch (state.Value) + { + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) + { + Width = 50, + RelativeSizeAxes = Axes.Y, + SelectedBeatmap = { BindTarget = Picker.Beatmap } + }; + break; + + case DownloadState.Downloading: + 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; + + default: + downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); + break; + } + } + + private class DownloadTracker : BeatmapDownloadTrackingComposite + { + public new Bindable State => base.State; + } + protected override OverlayTitle CreateTitle() => new BeatmapHeaderTitle(); protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector { - Current = Ruleset + Current = ruleset }; private class BeatmapHeaderTitle : OverlayTitle diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs deleted file mode 100644 index 916c21c010..0000000000 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ /dev/null @@ -1,313 +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.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Overlays.BeatmapListing.Panels; -using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Rulesets; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.BeatmapSet -{ - public class Header : BeatmapDownloadTrackingComposite - { - private const float transition_duration = 200; - private const float buttons_height = 45; - private const float buttons_spacing = 5; - - private readonly UpdateableBeatmapSetCover cover; - private readonly Box coverGradient; - private readonly OsuSpriteText title, artist; - private readonly AuthorInfo author; - private readonly ExplicitContentBeatmapPill explicitContentPill; - private readonly FillFlowContainer downloadButtonsContainer; - private readonly BeatmapAvailability beatmapAvailability; - private readonly BeatmapSetOnlineStatusPill onlineStatusPill; - public Details Details; - - public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); - - [Resolved] - private IAPIProvider api { get; set; } - - public BeatmapRulesetSelector RulesetSelector => beatmapSetHeader.RulesetSelector; - public readonly BeatmapPicker Picker; - - private readonly FavouriteButton favouriteButton; - private readonly FillFlowContainer fadeContent; - private readonly LoadingSpinner loading; - private readonly BeatmapSetHeader beatmapSetHeader; - - [Cached(typeof(IBindable))] - private readonly Bindable ruleset = new Bindable(); - - public Header() - { - ExternalLinkButton externalLink; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Masking = true; - - EdgeEffect = new EdgeEffectParameters - { - Colour = Color4.Black.Opacity(0.25f), - Type = EdgeEffectType.Shadow, - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - beatmapSetHeader = new BeatmapSetHeader - { - Ruleset = { BindTarget = ruleset }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - cover = new UpdateableBeatmapSetCover - { - RelativeSizeAxes = Axes.Both, - Masking = true, - }, - coverGradient = new Box - { - RelativeSizeAxes = Axes.Both - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding - { - Vertical = BeatmapSetOverlay.Y_PADDING, - Left = BeatmapSetOverlay.X_PADDING, - Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, - }, - Children = new Drawable[] - { - fadeContent = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = Picker = new BeatmapPicker(), - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = 15 }, - Children = new Drawable[] - { - title = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) - }, - externalLink = new ExternalLinkButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font - }, - explicitContentPill = new ExplicitContentBeatmapPill - { - Alpha = 0f, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Left = 10, Bottom = 4 }, - } - } - }, - artist = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), - Margin = new MarginPadding { Bottom = 20 } - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = author = new AuthorInfo(), - }, - beatmapAvailability = new BeatmapAvailability(), - new Container - { - RelativeSizeAxes = Axes.X, - Height = buttons_height, - Margin = new MarginPadding { Top = 10 }, - Children = new Drawable[] - { - favouriteButton = new FavouriteButton - { - BeatmapSet = { BindTarget = BeatmapSet } - }, - downloadButtonsContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, - Spacing = new Vector2(buttons_spacing), - }, - }, - }, - }, - }, - } - }, - loading = new LoadingSpinner - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.5f), - }, - new FillFlowContainer - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, - Direction = FillDirection.Vertical, - Spacing = new Vector2(10), - Children = new Drawable[] - { - onlineStatusPill = new BeatmapSetOnlineStatusPill - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - TextSize = 14, - TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } - }, - Details = new Details(), - }, - }, - }, - }, - } - }; - - Picker.Beatmap.ValueChanged += b => - { - Details.Beatmap = b.NewValue; - externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - onlineStatusPill.BackgroundColour = colourProvider.Background6; - - State.BindValueChanged(_ => updateDownloadButtons()); - - BeatmapSet.BindValueChanged(setInfo => - { - Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; - cover.BeatmapSet = setInfo.NewValue; - - if (setInfo.NewValue == null) - { - onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); - fadeContent.Hide(); - - loading.Show(); - - downloadButtonsContainer.FadeOut(transition_duration); - favouriteButton.FadeOut(transition_duration); - } - else - { - fadeContent.FadeIn(500, Easing.OutQuint); - - loading.Hide(); - - title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty; - artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty; - - explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; - - onlineStatusPill.FadeIn(500, Easing.OutQuint); - onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; - - downloadButtonsContainer.FadeIn(transition_duration); - favouriteButton.FadeIn(transition_duration); - - updateDownloadButtons(); - } - }, true); - } - - private void updateDownloadButtons() - { - if (BeatmapSet.Value == null) return; - - if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable) - { - downloadButtonsContainer.Clear(); - return; - } - - switch (State.Value) - { - case DownloadState.LocallyAvailable: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new BeatmapPanelDownloadButton(BeatmapSet.Value) - { - Width = 50, - RelativeSizeAxes = Axes.Y, - SelectedBeatmap = { BindTarget = Picker.Beatmap } - }; - break; - - case DownloadState.Downloading: - 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; - - default: - downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); - break; - } - } - } -} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index bbec62a85a..86f0f4f614 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -19,15 +19,12 @@ using osuTK; namespace osu.Game.Overlays { - public class BeatmapSetOverlay : FullscreenOverlay // we don't provide a standard header for now. + public class BeatmapSetOverlay : FullscreenOverlay { public const float X_PADDING = 40; public const float Y_PADDING = 25; public const float RIGHT_WIDTH = 275; - //todo: should be an OverlayHeader? or maybe not? - protected new readonly Header Header; - [Resolved] private RulesetStore rulesets { get; set; } @@ -39,7 +36,7 @@ namespace osu.Game.Overlays private readonly Box background; public BeatmapSetOverlay() - : base(OverlayColourScheme.Blue, null) + : base(OverlayColourScheme.Blue, new BeatmapSetHeader()) { OverlayScrollContainer scroll; Info info; @@ -72,7 +69,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - Header = new Header(), + Header, info = new Info() } }, From 1e99357a9790884a7a84e4c052a6ab65921586a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Jan 2021 12:13:27 +0900 Subject: [PATCH 101/385] Update build configurations to net5.0 paths --- .../.idea/runConfigurations/Benchmarks.xml | 6 +++--- .../runConfigurations/CatchRuleset__Tests_.xml | 6 +++--- .../runConfigurations/ManiaRuleset__Tests_.xml | 6 +++--- .../runConfigurations/OsuRuleset__Tests_.xml | 6 +++--- .../runConfigurations/TaikoRuleset__Tests_.xml | 6 +++--- .../.idea/runConfigurations/Tournament.xml | 6 +++--- .../runConfigurations/Tournament__Tests_.xml | 6 +++--- .../.idea/runConfigurations/osu_.xml | 6 +++--- .../.idea/runConfigurations/osu___Tests_.xml | 6 +++--- .../runConfigurations/osu___legacy_osuTK_.xml | 6 +++--- .vscode/launch.json | 18 +++++++++--------- .../.vscode/launch.json | 4 ++-- .../.vscode/launch.json | 4 ++-- .../.vscode/launch.json | 4 ++-- .../.vscode/launch.json | 4 ++-- osu.Game.Tournament.Tests/.vscode/launch.json | 4 ++-- 16 files changed, 49 insertions(+), 49 deletions(-) diff --git a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml index 1815c271b4..8fa7608b8e 100644 --- a/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml +++ b/.idea/.idea.osu.Desktop/.idea/runConfigurations/Benchmarks.xml @@ -1,8 +1,8 @@ - public class BookmarkPart : TimelinePart { - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs index ceccbffc9c..e8a4b5c8c7 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; @@ -14,10 +13,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// public class BreakPart : TimelinePart { - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); - foreach (var breakPeriod in beatmap.Beatmap.Breaks) + foreach (var breakPeriod in beatmap.Breaks) Add(new BreakVisualisation(breakPeriod)); } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs index e76ab71e54..70afc1e308 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs @@ -4,7 +4,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts @@ -16,12 +15,12 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { private readonly IBindableList controlPointGroups = new BindableList(); - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); controlPointGroups.UnbindAll(); - controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 5a2214509c..d551333616 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -2,15 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osuTK; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Graphics; +using osuTK; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { @@ -54,9 +53,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts scheduledSeek?.Cancel(); scheduledSeek = Schedule(() => { - if (Beatmap.Value == null) - return; - float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); editorClock.SeekSmoothlyTo(markerPos / DrawWidth * editorClock.TrackLength); }); @@ -68,7 +64,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts marker.X = (float)editorClock.CurrentTime; } - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { // block base call so we don't clear our marker (can be reused on beatmap change). } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 5b8f7c747b..5aba81aa7d 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -21,7 +21,10 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts /// public class TimelinePart : Container where T : Drawable { - protected readonly IBindable Beatmap = new Bindable(); + private readonly IBindable beatmap = new Bindable(); + + [Resolved] + protected EditorBeatmap EditorBeatmap { get; private set; } protected readonly IBindable Track = new Bindable(); @@ -33,10 +36,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { AddInternal(this.content = content ?? new Container { RelativeSizeAxes = Axes.Both }); - Beatmap.ValueChanged += b => + beatmap.ValueChanged += b => { updateRelativeChildSize(); - LoadBeatmap(b.NewValue); }; Track.ValueChanged += _ => updateRelativeChildSize(); @@ -45,24 +47,26 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts [BackgroundDependencyLoader] private void load(IBindable beatmap, EditorClock clock) { - Beatmap.BindTo(beatmap); + this.beatmap.BindTo(beatmap); + LoadBeatmap(EditorBeatmap); + Track.BindTo(clock.Track); } private void updateRelativeChildSize() { // the track may not be loaded completely (only has a length once it is). - if (!Beatmap.Value.Track.IsLoaded) + if (!beatmap.Value.Track.IsLoaded) { content.RelativeChildSize = Vector2.One; Schedule(updateRelativeChildSize); return; } - content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); + content.RelativeChildSize = new Vector2((float)Math.Max(1, beatmap.Value.Track.Length), 1); } - protected virtual void LoadBeatmap(WorkingBeatmap beatmap) + protected virtual void LoadBeatmap(EditorBeatmap beatmap) { content.Clear(); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs index 13191df13c..18600bcdee 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -23,12 +22,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both; } - protected override void LoadBeatmap(WorkingBeatmap beatmap) + protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); controlPointGroups.UnbindAll(); - controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups); + controlPointGroups.BindTo(beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((sender, args) => { switch (args.Action) From f3061a8e837e4a59522d9a4faeac1b0e9f98aa00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 18:47:41 +0900 Subject: [PATCH 186/385] Update squirrel to fix incorrect desktop icon creation on install --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 4554f8b83a..e201b250d4 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,7 +25,7 @@ - + From 439f03e3b3d78dbdbc8ba3d6db559c13043f1e86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Jan 2021 19:25:38 +0900 Subject: [PATCH 187/385] Fix failing test due to missing dependency --- .../Visual/Editing/TestSceneEditorSummaryTimeline.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index 3adc1bd425..94a9fd7b35 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -5,6 +5,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osuTK; @@ -13,6 +15,9 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneEditorSummaryTimeline : EditorClockTestScene { + [Cached(typeof(EditorBeatmap))] + private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + [BackgroundDependencyLoader] private void load() { From f89eb7d75db7982d3cdc6200b4737c630094eb87 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:22:37 +0100 Subject: [PATCH 188/385] Split and rename TournamentModDisplay component --- .../Components/TournamentBeatmapPanel.cs | 64 +++---------------- .../Components/TournamentModDisplay.cs | 56 ++++++++++++++++ 2 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 osu.Game.Tournament/Components/TournamentModDisplay.cs diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 92ac123097..2ed99d2fb5 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -9,14 +9,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Rulesets; -using osu.Game.Screens.Play.HUD; using osu.Game.Tournament.Models; using osuTK.Graphics; @@ -25,7 +22,7 @@ namespace osu.Game.Tournament.Components public class TournamentBeatmapPanel : CompositeDrawable { public readonly BeatmapInfo Beatmap; - private readonly string mods; + private readonly string mod; private const float horizontal_padding = 10; private const float vertical_padding = 10; @@ -40,7 +37,7 @@ namespace osu.Game.Tournament.Components if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); Beatmap = beatmap; - this.mods = mods; + this.mod = mods; Width = 400; Height = HEIGHT; } @@ -124,13 +121,16 @@ namespace osu.Game.Tournament.Components }, }); - if (!string.IsNullOrEmpty(mods)) + if (!string.IsNullOrEmpty(mod)) { - AddInternal(new ModSprite + AddInternal(new TournamentModDisplay { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Mod = mods + Margin = new MarginPadding(10), + Width = 60, + RelativeSizeAxes = Axes.Y, + ModAcronym = mod }); } } @@ -184,53 +184,5 @@ namespace osu.Game.Tournament.Components Alpha = 1; } } - - private class ModSprite : Container - { - public string Mod; - - [Resolved] - private LadderInfo ladderInfo { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } - - public ModSprite() - { - Margin = new MarginPadding(10); - Width = 60; - RelativeSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - var texture = textures.Get($"mods/{Mod}"); - - if (texture != null) - { - Child = new Sprite - { - FillMode = FillMode.Fit, - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Texture = texture - }; - } - else - { - Child = new ModDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Current = - { - Value = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().Where(mod => mod.Acronym == Mod).ToArray() - } - }; - } - } - } } } diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs new file mode 100644 index 0000000000..a22969c20d --- /dev/null +++ b/osu.Game.Tournament/Components/TournamentModDisplay.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.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets; +using osu.Game.Rulesets.UI; +using osu.Game.Tournament.Models; +using osuTK; + +namespace osu.Game.Tournament.Components +{ + public class TournamentModDisplay : CompositeDrawable + { + public string ModAcronym; + + [Resolved] + private LadderInfo ladderInfo { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + var texture = textures.Get($"mods/{ModAcronym}"); + + if (texture != null) + { + AddInternal(new Sprite + { + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Texture = texture + }); + } + else + { + var mod = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + + AddInternal(new ModIcon(mod) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.5f) + }); + } + } + } +} From 74310da7cf550af33367a4dd0e3d49eebacf32a5 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:24:43 +0100 Subject: [PATCH 189/385] Change parameter to be singular mod instead of plural --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 2ed99d2fb5..e02709a045 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -32,12 +32,12 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(BeatmapInfo beatmap, string mods = null) + public TournamentBeatmapPanel(BeatmapInfo beatmap, string mod = null) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); Beatmap = beatmap; - this.mod = mods; + this.mod = mod; Width = 400; Height = HEIGHT; } From ca08a19c409e4a454a0d6a66d7ce57d2ea845228 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 13:28:46 +0100 Subject: [PATCH 190/385] Rename mod to modIcon --- osu.Game.Tournament/Components/TournamentModDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index a22969c20d..827b3d6a69 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -42,9 +42,9 @@ namespace osu.Game.Tournament.Components } else { - var mod = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var modIcon = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); - AddInternal(new ModIcon(mod) + AddInternal(new ModIcon(modIcon) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 07bd9013585ebae924cf44c501b079bb9d9b01b0 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:20:58 +0100 Subject: [PATCH 191/385] Add visual test for Tournament Mod Display --- .../TestSceneTournamentModDisplay.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs new file mode 100644 index 0000000000..9689ecd4ae --- /dev/null +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -0,0 +1,66 @@ +// 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.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Tests.Components +{ + public class TestSceneTournamentModDisplay : TournamentTestScene + { + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private FillFlowContainer fillFlow; + + private BeatmapInfo beatmap; + + [BackgroundDependencyLoader] + private void load() + { + var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 490154 }); + req.Success += success; + api.Queue(req); + + Add(fillFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Full, + Spacing = new osuTK.Vector2(10) + }); + } + + [Test] + public void TestModDisplay() + { + AddUntilStep("beatmap is available", () => beatmap != null); + AddStep("add maps with available mods for ruleset", () => displayForRuleset(Ladder.Ruleset.Value.ID ?? 0)); + } + + private void displayForRuleset(int rulesetId) + { + fillFlow.Clear(); + var mods = rulesets.GetRuleset(rulesetId).CreateInstance().GetAllMods(); + + foreach (var mod in mods) + { + fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)); + } + } + + private void success(APIBeatmap apiBeatmap) => beatmap = apiBeatmap.ToBeatmap(rulesets); + } +} From 5e0ccb6c91d3e1d55968882b80d3ba0eca1971a0 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:21:22 +0100 Subject: [PATCH 192/385] Remove unncessary test step --- .../TestSceneTournamentModDisplay.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index 9689ecd4ae..b4d9fa4222 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -43,24 +42,19 @@ namespace osu.Game.Tournament.Tests.Components }); } - [Test] - public void TestModDisplay() + private void success(APIBeatmap apiBeatmap) { - AddUntilStep("beatmap is available", () => beatmap != null); - AddStep("add maps with available mods for ruleset", () => displayForRuleset(Ladder.Ruleset.Value.ID ?? 0)); - } - - private void displayForRuleset(int rulesetId) - { - fillFlow.Clear(); - var mods = rulesets.GetRuleset(rulesetId).CreateInstance().GetAllMods(); + beatmap = apiBeatmap.ToBeatmap(rulesets); + var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods(); foreach (var mod in mods) { - fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)); + fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); } } - - private void success(APIBeatmap apiBeatmap) => beatmap = apiBeatmap.ToBeatmap(rulesets); } } From 6a85f5ca8bf2a9506ed55d9a60f870b820326e2b Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:21:53 +0100 Subject: [PATCH 193/385] Add null checks to prevent nullrefexception in automated test --- osu.Game.Tournament/Components/TournamentModDisplay.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 827b3d6a69..e91c27345e 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -42,7 +42,15 @@ namespace osu.Game.Tournament.Components } else { - var modIcon = rulesets.GetRuleset(ladderInfo.Ruleset.Value.ID ?? 0).CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var ruleset = rulesets.AvailableRulesets.FirstOrDefault(r => r == ladderInfo.Ruleset.Value); + + if (ruleset == null) + return; + + var modIcon = ruleset.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + + if (modIcon == null) + return; AddInternal(new ModIcon(modIcon) { From a741d91aed91f338ffecd3a4596b551c8804b52e Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 14:57:35 +0100 Subject: [PATCH 194/385] use null propragtor for Ruleset.Value and rulset instead of null checks --- .../Components/TournamentModDisplay.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index e91c27345e..369a58858e 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -18,14 +18,11 @@ namespace osu.Game.Tournament.Components { public string ModAcronym; - [Resolved] - private LadderInfo ladderInfo { get; set; } - [Resolved] private RulesetStore rulesets { get; set; } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(TextureStore textures, LadderInfo ladderInfo) { var texture = textures.Get($"mods/{ModAcronym}"); @@ -42,12 +39,8 @@ namespace osu.Game.Tournament.Components } else { - var ruleset = rulesets.AvailableRulesets.FirstOrDefault(r => r == ladderInfo.Ruleset.Value); - - if (ruleset == null) - return; - - var modIcon = ruleset.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); if (modIcon == null) return; From b036f0165a077bc49cb0fd9d7e8713fb6a18fd89 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 25 Jan 2021 15:47:31 +0100 Subject: [PATCH 195/385] move value set to constructor and make private readonly --- .../Components/TournamentBeatmapPanel.cs | 3 +-- .../Components/TournamentModDisplay.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index e02709a045..8cc4566c08 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -123,14 +123,13 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mod)) { - AddInternal(new TournamentModDisplay + AddInternal(new TournamentModDisplay(mod) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding(10), Width = 60, RelativeSizeAxes = Axes.Y, - ModAcronym = mod }); } } diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 369a58858e..3df8550667 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -16,15 +16,20 @@ namespace osu.Game.Tournament.Components { public class TournamentModDisplay : CompositeDrawable { - public string ModAcronym; + private readonly string modAcronym; [Resolved] private RulesetStore rulesets { get; set; } + public TournamentModDisplay(string mod) + { + modAcronym = mod; + } + [BackgroundDependencyLoader] private void load(TextureStore textures, LadderInfo ladderInfo) { - var texture = textures.Get($"mods/{ModAcronym}"); + var texture = textures.Get($"mods/{modAcronym}"); if (texture != null) { @@ -40,7 +45,7 @@ namespace osu.Game.Tournament.Components else { var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == ModAcronym); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); if (modIcon == null) return; From 9efce5717f40e2ade16c4dee2cb89501d6c27c0d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 22:11:50 +0300 Subject: [PATCH 196/385] Fix beatmap listing placeholder disappearing on second time display --- osu.Game/Overlays/BeatmapListingOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 2f7f21e403..b65eaad0a2 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -176,6 +176,9 @@ namespace osu.Game.Overlays loadingLayer.Hide(); lastFetchDisplayedTime = Time.Current; + if (content == currentContent) + return; + var lastContent = currentContent; if (lastContent != null) From 9312de7c2387f4534090304550f3910c54b98e39 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 23:40:26 +0300 Subject: [PATCH 197/385] Move online beatmap listing overlay to separate test scene --- ...ingOverlay.cs => TestSceneOnlineBeatmapListingOverlay.cs} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneBeatmapListingOverlay.cs => TestSceneOnlineBeatmapListingOverlay.cs} (80%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs similarity index 80% rename from osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs rename to osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs index 6cb1687d1f..fe1701a554 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneOnlineBeatmapListingOverlay.cs @@ -6,13 +6,14 @@ using NUnit.Framework; namespace osu.Game.Tests.Visual.Online { - public class TestSceneBeatmapListingOverlay : OsuTestScene + [Description("uses online API")] + public class TestSceneOnlineBeatmapListingOverlay : OsuTestScene { protected override bool UseOnlineAPI => true; private readonly BeatmapListingOverlay overlay; - public TestSceneBeatmapListingOverlay() + public TestSceneOnlineBeatmapListingOverlay() { Add(overlay = new BeatmapListingOverlay()); } From 75d6dbdbb7290c215505e408f15781b96128d929 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 22:18:23 +0300 Subject: [PATCH 198/385] Fix beatmap listing placeholder potentially getting disposed --- osu.Game/Overlays/BeatmapListingOverlay.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 0c9c995dd6..2f7f21e403 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -180,18 +180,24 @@ namespace osu.Game.Overlays if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint).Expire(); + lastContent.FadeOut(100, Easing.OutQuint); // Consider the case when the new content is smaller than the last content. // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => + { + panelTarget.Remove(lastContent); + + // the content may be reused again (e.g. notFoundContent), clear Y-axis bypass for displaying back properly. + lastContent.BypassAutoSizeAxes = Axes.None; + }); } if (!content.IsAlive) panelTarget.Add(content); - content.FadeIn(200, Easing.OutQuint); + content.FadeInFromZero(200, Easing.OutQuint); currentContent = content; } From c317d6016967a321c70d02ee81d012129e8b5767 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 25 Jan 2021 23:41:05 +0300 Subject: [PATCH 199/385] Add offline test scene for beatmap listing overlay --- .../Online/TestSceneBeatmapListingOverlay.cs | 81 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmapSet.cs | 2 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs new file mode 100644 index 0000000000..1349264bf9 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -0,0 +1,81 @@ +// 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.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapListing; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneBeatmapListingOverlay : OsuTestScene + { + private readonly List setsForResponse = new List(); + + private BeatmapListingOverlay overlay; + + [BackgroundDependencyLoader] + private void load() + { + Child = overlay = new BeatmapListingOverlay { State = { Value = Visibility.Visible } }; + + ((DummyAPIAccess)API).HandleRequest = req => + { + if (req is SearchBeatmapSetsRequest searchBeatmapSetsRequest) + { + searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse + { + BeatmapSets = setsForResponse, + }); + } + }; + } + + [Test] + public void TestNoBeatmapsPlaceholder() + { + AddStep("fetch for 0 beatmaps", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + + AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); + AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any()); + + AddStep("fetch for 0 beatmaps", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + + // fetch once more to ensure nothing happens in displaying placeholder again when it already is present. + AddStep("fetch for 0 beatmaps again", () => fetchFor()); + AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); + } + + private void fetchFor(params BeatmapSetInfo[] beatmaps) + { + setsForResponse.Clear(); + setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b))); + + // trigger arbitrary change for fetching. + overlay.ChildrenOfType().Single().Query.TriggerChange(); + } + + private class TestAPIBeatmapSet : APIBeatmapSet + { + private readonly BeatmapSetInfo beatmapSet; + + public TestAPIBeatmapSet(BeatmapSetInfo beatmapSet) + { + this.beatmapSet = beatmapSet; + } + + public override BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) => beatmapSet; + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index bd1800e9f7..45d9c9405f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -81,7 +81,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } - public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) + public virtual BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { var beatmapSet = new BeatmapSetInfo { diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index b65eaad0a2..c5cc0a9c85 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -211,7 +211,7 @@ namespace osu.Game.Overlays base.Dispose(isDisposing); } - private class NotFoundDrawable : CompositeDrawable + public class NotFoundDrawable : CompositeDrawable { public NotFoundDrawable() { From 0d8d0d685219e162e385f0bb506735d7fedcb2e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Jan 2021 01:03:29 +0300 Subject: [PATCH 200/385] Apply alternative way of fixing --- osu.Game/Overlays/BeatmapListingOverlay.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 2f7f21e403..fc90968ec4 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -178,21 +178,18 @@ namespace osu.Game.Overlays var lastContent = currentContent; - if (lastContent != null) + // "not found" placeholder is reused, only remove without disposing through expire. + if (lastContent == notFoundContent) + lastContent.FadeOut(100, Easing.OutQuint).Schedule(() => panelTarget.Remove(lastContent)); + else if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint); + lastContent.FadeOut(100, Easing.OutQuint).Expire(); // Consider the case when the new content is smaller than the last content. // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => - { - panelTarget.Remove(lastContent); - - // the content may be reused again (e.g. notFoundContent), clear Y-axis bypass for displaying back properly. - lastContent.BypassAutoSizeAxes = Axes.None; - }); + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); } if (!content.IsAlive) From 3307e8357ff6f919ff649c6e8036b12c5d4ffb77 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 26 Jan 2021 00:36:32 -0500 Subject: [PATCH 201/385] DrawableStoryboardSample event method override for SamplePlaybackDisabledChanged --- osu.Game/Skinning/PausableSkinnableSound.cs | 46 +++++++++---------- .../Drawables/DrawableStoryboardSample.cs | 14 +++--- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs index d8149e76c0..e2794938ad 100644 --- a/osu.Game/Skinning/PausableSkinnableSound.cs +++ b/osu.Game/Skinning/PausableSkinnableSound.cs @@ -18,14 +18,6 @@ namespace osu.Game.Skinning protected bool RequestedPlaying { get; private set; } - /// - /// Whether this is affected by - /// a higher-level 's state changes. - /// By default only looping samples are started/stopped on sample disable - /// to prevent one-time samples from cutting off abruptly. - /// - protected virtual bool AffectedBySamplePlaybackDisable => Looping; - public PausableSkinnableSound() { } @@ -51,24 +43,28 @@ namespace osu.Game.Skinning if (samplePlaybackDisabler != null) { samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); - samplePlaybackDisabled.BindValueChanged(disabled => + samplePlaybackDisabled.BindValueChanged(SamplePlaybackDisabledChanged); + } + } + + protected virtual void SamplePlaybackDisabledChanged(ValueChangedEvent disabled) + { + if (!RequestedPlaying) return; + + // let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off). + if (!Looping) return; + + cancelPendingStart(); + + if (disabled.NewValue) + base.Stop(); + else + { + // schedule so we don't start playing a sample which is no longer alive. + scheduledStart = Schedule(() => { - if (!RequestedPlaying) return; - if (!AffectedBySamplePlaybackDisable) return; - - cancelPendingStart(); - - if (disabled.NewValue) - base.Stop(); - else - { - // schedule so we don't start playing a sample which is no longer alive. - scheduledStart = Schedule(() => - { - if (RequestedPlaying) - base.Play(); - }); - } + if (RequestedPlaying) + base.Play(); }); } } diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index 5a800a71fd..db8428f062 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -21,12 +21,6 @@ namespace osu.Game.Storyboards.Drawables public override bool RemoveWhenNotAlive => false; - /// - /// Contrary to , all s are affected - /// by sample disables, as they are oftentimes longer-running sound effects. This also matches stable behaviour. - /// - protected override bool AffectedBySamplePlaybackDisable => true; - public DrawableStoryboardSample(StoryboardSampleInfo sampleInfo) : base(sampleInfo) { @@ -48,6 +42,14 @@ namespace osu.Game.Storyboards.Drawables } } + protected override void SamplePlaybackDisabledChanged(ValueChangedEvent disabled) + { + if (!RequestedPlaying) return; + + if (disabled.NewValue) + Stop(); + } + protected override void Update() { base.Update(); From ca0242debef0c77bde87d7f4fecdc84f3b65a8b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 15:42:48 +0900 Subject: [PATCH 202/385] Tidy up logic to correctly expire in cases where expiry is expected --- osu.Game/Overlays/BeatmapListingOverlay.cs | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index fc90968ec4..de566c92cb 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -178,24 +178,29 @@ namespace osu.Game.Overlays var lastContent = currentContent; - // "not found" placeholder is reused, only remove without disposing through expire. - if (lastContent == notFoundContent) - lastContent.FadeOut(100, Easing.OutQuint).Schedule(() => panelTarget.Remove(lastContent)); - else if (lastContent != null) + if (lastContent != null) { - lastContent.FadeOut(100, Easing.OutQuint).Expire(); + var transform = lastContent.FadeOut(100, Easing.OutQuint); - // Consider the case when the new content is smaller than the last content. - // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. - // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. - // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. - lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => panelTarget.Remove(lastContent)); + if (lastContent == notFoundContent) + { + // not found display may be used multiple times, so don't expire/dispose it. + transform.Schedule(() => panelTarget.Remove(lastContent)); + } + else + { + // Consider the case when the new content is smaller than the last content. + // If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird. + // At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0. + // To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so. + lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y).Then().Schedule(() => lastContent.Expire()); + } } if (!content.IsAlive) panelTarget.Add(content); - content.FadeInFromZero(200, Easing.OutQuint); + content.FadeInFromZero(200, Easing.OutQuint); currentContent = content; } From 60ae87ec383645e61194bf51f19e94d55a342023 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:25:49 +0900 Subject: [PATCH 203/385] Add MessagePack package --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2b8f81532d..3e971d9d4f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,6 +22,7 @@ + From e4fc6041635c4aebfbb791c6e671324ad9156abf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:26:03 +0900 Subject: [PATCH 204/385] Setup all multiplayer model classes for MessagePack support --- osu.Game/Online/API/APIMod.cs | 7 ++++++- osu.Game/Online/Multiplayer/MultiplayerRoom.cs | 10 +++++++++- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 7 +++++++ osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 8 +++++++- osu.Game/Online/Rooms/BeatmapAvailability.cs | 6 +++++- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index c8b76b9685..69ce3825ee 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Humanizer; +using MessagePack; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Configuration; @@ -13,16 +14,20 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Online.API { + [MessagePackObject] public class APIMod : IMod { [JsonProperty("acronym")] + [Key(0)] public string Acronym { get; set; } [JsonProperty("settings")] + [Key(1)] public Dictionary Settings { get; set; } = new Dictionary(); [JsonConstructor] - private APIMod() + [SerializationConstructor] + public APIMod() { } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs index 12fcf25ace..c5fa6253ed 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoom.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoom.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; namespace osu.Game.Online.Multiplayer @@ -13,35 +14,42 @@ namespace osu.Game.Online.Multiplayer /// A multiplayer room. /// [Serializable] + [MessagePackObject] public class MultiplayerRoom { /// /// The ID of the room, used for database persistence. /// + [Key(0)] public readonly long RoomID; /// /// The current state of the room (ie. whether it is in progress or otherwise). /// + [Key(1)] public MultiplayerRoomState State { get; set; } /// /// All currently enforced game settings for this room. /// + [Key(2)] public MultiplayerRoomSettings Settings { get; set; } = new MultiplayerRoomSettings(); /// /// All users currently in this room. /// + [Key(3)] public List Users { get; set; } = new List(); /// /// The host of this room, in control of changing room settings. /// + [Key(4)] public MultiplayerRoomUser? Host { get; set; } [JsonConstructor] - public MultiplayerRoom(in long roomId) + [SerializationConstructor] + public MultiplayerRoom(long roomId) { RoomID = roomId; } diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 857b38ea60..0ead5db84c 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -7,22 +7,29 @@ using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using MessagePack; using osu.Game.Online.API; namespace osu.Game.Online.Multiplayer { [Serializable] + [MessagePackObject] public class MultiplayerRoomSettings : IEquatable { + [Key(0)] public int BeatmapID { get; set; } + [Key(1)] public int RulesetID { get; set; } + [Key(2)] public string BeatmapChecksum { get; set; } = string.Empty; + [Key(3)] public string Name { get; set; } = "Unnamed room"; [NotNull] + [Key(4)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); public bool Equals(MultiplayerRoomSettings other) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 2590acbc81..b300be9f60 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using MessagePack; using Newtonsoft.Json; using osu.Game.Online.Rooms; using osu.Game.Users; @@ -11,21 +12,26 @@ using osu.Game.Users; namespace osu.Game.Online.Multiplayer { [Serializable] + [MessagePackObject] public class MultiplayerRoomUser : IEquatable { + [Key(0)] public readonly int UserID; + [Key(1)] public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle; /// /// The availability state of the current beatmap. /// + [Key(2)] public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); + [IgnoreMember] public User? User { get; set; } [JsonConstructor] - public MultiplayerRoomUser(in int userId) + public MultiplayerRoomUser(int userId) { UserID = userId; } diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index e7dbc5f436..38bd236718 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 MessagePack; using Newtonsoft.Json; namespace osu.Game.Online.Rooms @@ -9,20 +10,23 @@ namespace osu.Game.Online.Rooms /// /// The local availability information about a certain beatmap for the client. /// + [MessagePackObject] public class BeatmapAvailability : IEquatable { /// /// The beatmap's availability state. /// + [Key(0)] public readonly DownloadState State; /// /// The beatmap's downloading progress, null when not in state. /// + [Key(1)] public readonly double? DownloadProgress; [JsonConstructor] - private BeatmapAvailability(DownloadState state, double? downloadProgress = null) + public BeatmapAvailability(DownloadState state, double? downloadProgress = null) { State = state; DownloadProgress = downloadProgress; From 9537090d28bb29994e5a2902c4091e69bc15856b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 16:39:35 +0900 Subject: [PATCH 205/385] Setup all spectator model classes for MessagePack --- osu.Game/Online/Spectator/FrameDataBundle.cs | 4 ++++ osu.Game/Online/Spectator/FrameHeader.cs | 10 +++++++++- osu.Game/Online/Spectator/SpectatorState.cs | 5 +++++ osu.Game/Replays/Legacy/LegacyReplayFrame.cs | 13 +++++++++++++ osu.Game/Rulesets/Replays/ReplayFrame.cs | 4 ++++ 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/FrameDataBundle.cs b/osu.Game/Online/Spectator/FrameDataBundle.cs index a8d0434324..0e59cdf4ce 100644 --- a/osu.Game/Online/Spectator/FrameDataBundle.cs +++ b/osu.Game/Online/Spectator/FrameDataBundle.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; using osu.Game.Replays.Legacy; using osu.Game.Scoring; @@ -12,10 +13,13 @@ using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class FrameDataBundle { + [Key(0)] public FrameHeader Header { get; set; } + [Key(1)] public IEnumerable Frames { get; set; } public FrameDataBundle(ScoreInfo score, IEnumerable frames) diff --git a/osu.Game/Online/Spectator/FrameHeader.cs b/osu.Game/Online/Spectator/FrameHeader.cs index 135b356eda..adfcbcd95a 100644 --- a/osu.Game/Online/Spectator/FrameHeader.cs +++ b/osu.Game/Online/Spectator/FrameHeader.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using MessagePack; using Newtonsoft.Json; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -12,31 +13,37 @@ using osu.Game.Scoring; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class FrameHeader { /// /// The current accuracy of the score. /// + [Key(0)] public double Accuracy { get; set; } /// /// The current combo of the score. /// + [Key(1)] public int Combo { get; set; } /// /// The maximum combo achieved up to the current point in time. /// + [Key(2)] public int MaxCombo { get; set; } /// /// Cumulative hit statistics. /// + [Key(3)] public Dictionary Statistics { get; set; } /// /// The time at which this frame was received by the server. /// + [Key(4)] public DateTimeOffset ReceivedTime { get; set; } /// @@ -54,7 +61,8 @@ namespace osu.Game.Online.Spectator } [JsonConstructor] - public FrameHeader(int combo, int maxCombo, double accuracy, Dictionary statistics, DateTimeOffset receivedTime) + [SerializationConstructor] + public FrameHeader(double accuracy, int combo, int maxCombo, Dictionary statistics, DateTimeOffset receivedTime) { Combo = combo; MaxCombo = maxCombo; diff --git a/osu.Game/Online/Spectator/SpectatorState.cs b/osu.Game/Online/Spectator/SpectatorState.cs index 101ce3d5d5..96a875bc14 100644 --- a/osu.Game/Online/Spectator/SpectatorState.cs +++ b/osu.Game/Online/Spectator/SpectatorState.cs @@ -5,18 +5,23 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using MessagePack; using osu.Game.Online.API; namespace osu.Game.Online.Spectator { [Serializable] + [MessagePackObject] public class SpectatorState : IEquatable { + [Key(0)] public int? BeatmapID { get; set; } + [Key(1)] public int? RulesetID { get; set; } [NotNull] + [Key(2)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); public bool Equals(SpectatorState other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; diff --git a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs index 74bacae9e1..ab9ccda9b9 100644 --- a/osu.Game/Replays/Legacy/LegacyReplayFrame.cs +++ b/osu.Game/Replays/Legacy/LegacyReplayFrame.cs @@ -1,38 +1,51 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using MessagePack; using Newtonsoft.Json; using osu.Game.Rulesets.Replays; using osuTK; namespace osu.Game.Replays.Legacy { + [MessagePackObject] public class LegacyReplayFrame : ReplayFrame { [JsonIgnore] + [IgnoreMember] public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); + [Key(1)] public float? MouseX; + + [Key(2)] public float? MouseY; [JsonIgnore] + [IgnoreMember] public bool MouseLeft => MouseLeft1 || MouseLeft2; [JsonIgnore] + [IgnoreMember] public bool MouseRight => MouseRight1 || MouseRight2; [JsonIgnore] + [IgnoreMember] public bool MouseLeft1 => ButtonState.HasFlag(ReplayButtonState.Left1); [JsonIgnore] + [IgnoreMember] public bool MouseRight1 => ButtonState.HasFlag(ReplayButtonState.Right1); [JsonIgnore] + [IgnoreMember] public bool MouseLeft2 => ButtonState.HasFlag(ReplayButtonState.Left2); [JsonIgnore] + [IgnoreMember] public bool MouseRight2 => ButtonState.HasFlag(ReplayButtonState.Right2); + [Key(3)] public ReplayButtonState ButtonState; public LegacyReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs index 85e068ae79..7de53211a2 100644 --- a/osu.Game/Rulesets/Replays/ReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs @@ -1,10 +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 MessagePack; + namespace osu.Game.Rulesets.Replays { + [MessagePackObject] public class ReplayFrame { + [Key(0)] public double Time; public ReplayFrame() From 20cfa991bffbe1cb4255b6cc45cda39b16cc28f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 17:41:21 +0900 Subject: [PATCH 206/385] Switch clients to MessagePack mode --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 50dc8f661c..3221456e75 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online.Multiplayer { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }) - .AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) + .AddMessagePackProtocol() .Build(); // this is kind of SILLY diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 344b73f3d9..cc866b7ad9 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -121,7 +121,7 @@ namespace osu.Game.Online.Spectator { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }) - .AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) + .AddMessagePackProtocol() .Build(); // until strong typed client support is added, each method must be manually bound (see https://github.com/dotnet/aspnetcore/issues/15198) From 15885c17af58234c63ea154178f2156854e95bd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 18:07:43 +0900 Subject: [PATCH 207/385] Remove unused usings --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 1 - osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 3221456e75..0d779232d0 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index cc866b7ad9..dac2131035 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; From a5f3418e561efcf05800d4d2d61f9182091467a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:11:19 +0900 Subject: [PATCH 208/385] Avoid tooltip display --- .../Components/TournamentModDisplay.cs | 2 +- osu.Game/Overlays/Mods/ModButton.cs | 16 +++------------- osu.Game/Rulesets/UI/ModIcon.cs | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModDisplay.cs index 3df8550667..fa9ee7edff 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModDisplay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tournament.Components if (modIcon == null) return; - AddInternal(new ModIcon(modIcon) + AddInternal(new ModIcon(modIcon, false) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index ab8efdabcc..8e0d1f5bbd 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -236,13 +236,13 @@ namespace osu.Game.Overlays.Mods { iconsContainer.AddRange(new[] { - backgroundIcon = new PassThroughTooltipModIcon(Mods[1]) + backgroundIcon = new ModIcon(Mods[1], false) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, Position = new Vector2(1.5f), }, - foregroundIcon = new PassThroughTooltipModIcon(Mods[0]) + foregroundIcon = new ModIcon(Mods[0], false) { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, @@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Mods } else { - iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod) + iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -297,15 +297,5 @@ namespace osu.Game.Overlays.Mods Mod = mod; } - - private class PassThroughTooltipModIcon : ModIcon - { - public override string TooltipText => null; - - public PassThroughTooltipModIcon(Mod mod) - : base(mod) - { - } - } } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 8ea6c74349..04a2e052fa 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -16,6 +16,9 @@ using osu.Framework.Bindables; namespace osu.Game.Rulesets.UI { + /// + /// Display the specified mod at a fixed size. + /// public class ModIcon : Container, IHasTooltip { public readonly BindableBool Selected = new BindableBool(); @@ -28,9 +31,10 @@ namespace osu.Game.Rulesets.UI private readonly ModType type; - public virtual string TooltipText => mod.IconTooltip; + public virtual string TooltipText => showTooltip ? mod.IconTooltip : null; private Mod mod; + private readonly bool showTooltip; public Mod Mod { @@ -42,9 +46,15 @@ namespace osu.Game.Rulesets.UI } } - public ModIcon(Mod mod) + /// + /// Construct a new instance. + /// + /// The mod to be displayed + /// Whether a tooltip describing the mod should display on hover. + public ModIcon(Mod mod, bool showTooltip = true) { this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); + this.showTooltip = showTooltip; type = mod.Type; From 64a3c712aa8be74bb2d32fddeb55fc364a3cb0fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:15:19 +0900 Subject: [PATCH 209/385] Rename class and add xmldoc --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- .../{TournamentModDisplay.cs => TournamentModIcon.cs} | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) rename osu.Game.Tournament/Components/{TournamentModDisplay.cs => TournamentModIcon.cs} (86%) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 8cc4566c08..d1197b1a61 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -123,7 +123,7 @@ namespace osu.Game.Tournament.Components if (!string.IsNullOrEmpty(mod)) { - AddInternal(new TournamentModDisplay(mod) + AddInternal(new TournamentModIcon(mod) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, diff --git a/osu.Game.Tournament/Components/TournamentModDisplay.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs similarity index 86% rename from osu.Game.Tournament/Components/TournamentModDisplay.cs rename to osu.Game.Tournament/Components/TournamentModIcon.cs index fa9ee7edff..b53ecc02f8 100644 --- a/osu.Game.Tournament/Components/TournamentModDisplay.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -14,16 +14,19 @@ using osuTK; namespace osu.Game.Tournament.Components { - public class TournamentModDisplay : CompositeDrawable + /// + /// Mod icon displayed in tournament usages, allowing user overridden graphics. + /// + public class TournamentModIcon : CompositeDrawable { private readonly string modAcronym; [Resolved] private RulesetStore rulesets { get; set; } - public TournamentModDisplay(string mod) + public TournamentModIcon(string modAcronym) { - modAcronym = mod; + this.modAcronym = modAcronym; } [BackgroundDependencyLoader] From 81ab82fafe13df7aa513c5d64f3bb205feac6102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Jan 2021 19:16:38 +0900 Subject: [PATCH 210/385] Tidy up nesting --- .../Components/TournamentModIcon.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentModIcon.cs b/osu.Game.Tournament/Components/TournamentModIcon.cs index b53ecc02f8..43ac92d285 100644 --- a/osu.Game.Tournament/Components/TournamentModIcon.cs +++ b/osu.Game.Tournament/Components/TournamentModIcon.cs @@ -32,9 +32,9 @@ namespace osu.Game.Tournament.Components [BackgroundDependencyLoader] private void load(TextureStore textures, LadderInfo ladderInfo) { - var texture = textures.Get($"mods/{modAcronym}"); + var customTexture = textures.Get($"mods/{modAcronym}"); - if (texture != null) + if (customTexture != null) { AddInternal(new Sprite { @@ -42,24 +42,24 @@ namespace osu.Game.Tournament.Components RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Texture = texture + Texture = customTexture }); + + return; } - else + + var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); + var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); + + if (modIcon == null) + return; + + AddInternal(new ModIcon(modIcon, false) { - var ruleset = rulesets.GetRuleset(ladderInfo.Ruleset.Value?.ID ?? 0); - var modIcon = ruleset?.CreateInstance().GetAllMods().FirstOrDefault(mod => mod.Acronym == modAcronym); - - if (modIcon == null) - return; - - AddInternal(new ModIcon(modIcon, false) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.5f) - }); - } + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.5f) + }); } } } From 690feb1c1e60aa563cdabad181344fda1dc5d62d Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 26 Jan 2021 23:08:51 -0500 Subject: [PATCH 211/385] Allow looping storyboard samples to follow the base samplePlaybackDisabled event logic. --- .../Storyboards/Drawables/DrawableStoryboardSample.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index db8428f062..9041c10640 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -46,8 +46,15 @@ namespace osu.Game.Storyboards.Drawables { if (!RequestedPlaying) return; - if (disabled.NewValue) - Stop(); + // non-looping storyboard samples should be stopped immediately when sample playback is disabled + if (!Looping) + { + if (disabled.NewValue) + Stop(); + } + else + base.SamplePlaybackDisabledChanged(disabled); + } protected override void Update() From ee89aa159cdc905c1c279080ac3d0333b105cfa4 Mon Sep 17 00:00:00 2001 From: Mysfit Date: Tue, 26 Jan 2021 23:12:26 -0500 Subject: [PATCH 212/385] Removed blank line --- osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index 9041c10640..fcbffda227 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -54,7 +54,6 @@ namespace osu.Game.Storyboards.Drawables } else base.SamplePlaybackDisabledChanged(disabled); - } protected override void Update() From a800955bb1c9519732f1bf2d72ab3bd0d5840bfb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 19:46:25 +0900 Subject: [PATCH 213/385] Add mod validation utilities --- osu.Game.Tests/Mods/ModValidationTest.cs | 64 +++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Utils/ModValidation.cs | 116 +++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 osu.Game.Tests/Mods/ModValidationTest.cs create mode 100644 osu.Game/Utils/ModValidation.cs diff --git a/osu.Game.Tests/Mods/ModValidationTest.cs b/osu.Game.Tests/Mods/ModValidationTest.cs new file mode 100644 index 0000000000..c7a7e242e9 --- /dev/null +++ b/osu.Game.Tests/Mods/ModValidationTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Moq; +using NUnit.Framework; +using osu.Game.Rulesets.Mods; +using osu.Game.Utils; + +namespace osu.Game.Tests.Mods +{ + [TestFixture] + public class ModValidationTest + { + [Test] + public void TestModIsCompatibleByItself() + { + var mod = new Mock(); + Assert.That(ModValidation.CheckCompatible(new[] { mod.Object })); + } + + [Test] + public void TestIncompatibleThroughTopLevel() + { + var mod1 = new Mock(); + var mod2 = new Mock(); + + mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() }); + + // Test both orderings. + Assert.That(ModValidation.CheckCompatible(new[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModValidation.CheckCompatible(new[] { mod2.Object, mod1.Object }), Is.False); + } + + [Test] + public void TestIncompatibleThroughMultiMod() + { + var mod1 = new Mock(); + + // The nested mod. + var mod2 = new Mock(); + mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() }); + + var multiMod = new MultiMod(new MultiMod(mod2.Object)); + + // Test both orderings. + Assert.That(ModValidation.CheckCompatible(new[] { multiMod, mod1.Object }), Is.False); + Assert.That(ModValidation.CheckCompatible(new[] { mod1.Object, multiMod }), Is.False); + } + + [Test] + public void TestAllowedThroughMostDerivedType() + { + var mod = new Mock(); + Assert.That(ModValidation.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); + } + + [Test] + public void TestNotAllowedThroughBaseType() + { + var mod = new Mock(); + Assert.That(ModValidation.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index c0c0578391..d29ed94b5f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -7,6 +7,7 @@ + WinExe diff --git a/osu.Game/Utils/ModValidation.cs b/osu.Game/Utils/ModValidation.cs new file mode 100644 index 0000000000..0c4d58ab2e --- /dev/null +++ b/osu.Game/Utils/ModValidation.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.TypeExtensions; +using osu.Game.Rulesets.Mods; + +#nullable enable + +namespace osu.Game.Utils +{ + /// + /// A set of utilities to validate combinations. + /// + public static class ModValidation + { + /// + /// Checks that all s are compatible with each-other, and that all appear within a set of allowed types. + /// + /// + /// The allowed types must contain exact types for the respective s to be allowed. + /// + /// The s to check. + /// The set of allowed types. + /// Whether all s are compatible with each-other and appear in the set of allowed types. + public static bool CheckCompatibleAndAllowed(IEnumerable combination, IEnumerable allowedTypes) + { + // Prevent multiple-enumeration. + var combinationList = combination as ICollection ?? combination.ToArray(); + return CheckCompatible(combinationList) && CheckAllowed(combinationList, allowedTypes); + } + + /// + /// Checks that all s in a combination are compatible with each-other. + /// + /// The combination to check. + /// Whether all s in the combination are compatible with each-other. + public static bool CheckCompatible(IEnumerable combination) + { + var incompatibleTypes = new HashSet(); + var incomingTypes = new HashSet(); + + foreach (var mod in combination.SelectMany(flattenMod)) + { + // Add the new mod incompatibilities, checking whether any match the existing mod types. + foreach (var t in mod.IncompatibleMods) + { + if (incomingTypes.Contains(t)) + return false; + + incompatibleTypes.Add(t); + } + + // Add the new mod types, checking whether any match the incompatible types. + foreach (var t in mod.GetType().EnumerateBaseTypes()) + { + if (incomingTypes.Contains(t)) + return false; + + incomingTypes.Add(t); + } + } + + return true; + } + + /// + /// Checks that all s in a combination appear within a set of allowed types. + /// + /// + /// The set of allowed types must contain exact types for the respective s to be allowed. + /// + /// The combination to check. + /// The set of allowed types. + /// Whether all s in the combination are allowed. + public static bool CheckAllowed(IEnumerable combination, IEnumerable allowedTypes) + { + var allowedSet = new HashSet(allowedTypes); + + return combination.SelectMany(flattenMod) + .All(m => allowedSet.Contains(m.GetType())); + } + + /// + /// Determines whether a is in a set of incompatible types. + /// + /// + /// A can be incompatible through its most-declared type or any of its base types. + /// + /// The to test. + /// The set of incompatible types. + /// Whether the given is incompatible. + private static bool isModIncompatible(Mod mod, ICollection incompatibleTypes) + => flattenMod(mod) + .SelectMany(m => m.GetType().EnumerateBaseTypes()) + .Any(incompatibleTypes.Contains); + + /// + /// Flattens a , returning a set of s in-place of any s. + /// + /// The to flatten. + /// A set of singular "flattened" s + private static IEnumerable flattenMod(Mod mod) + { + if (mod is MultiMod multi) + { + foreach (var m in multi.Mods.SelectMany(flattenMod)) + yield return m; + } + else + yield return mod; + } + } +} From f2fa51bf5e31e79c799fcf8fd17cd785280cde94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 19:59:28 +0900 Subject: [PATCH 214/385] Make ModSections overrideable --- osu.Game/Overlays/Mods/ModSection.cs | 18 +++++----- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 33 +++++++++++++++---- .../Mods/Sections/AutomationSection.cs | 19 ----------- .../Mods/Sections/ConversionSection.cs | 19 ----------- .../Sections/DifficultyIncreaseSection.cs | 19 ----------- .../Sections/DifficultyReductionSection.cs | 19 ----------- osu.Game/Overlays/Mods/Sections/FunSection.cs | 19 ----------- 7 files changed, 34 insertions(+), 112 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/Sections/AutomationSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/ConversionSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/FunSection.cs diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 573d1e5355..d70013602e 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -11,26 +11,23 @@ using System; using System.Linq; using System.Collections.Generic; using System.Threading; +using Humanizer; using osu.Framework.Input.Events; using osu.Game.Graphics; namespace osu.Game.Overlays.Mods { - public abstract class ModSection : Container + public class ModSection : Container { private readonly OsuSpriteText headerLabel; public FillFlowContainer ButtonsContainer { get; } public Action Action; - protected abstract Key[] ToggleKeys { get; } - public abstract ModType ModType { get; } - public string Header - { - get => headerLabel.Text; - set => headerLabel.Text = value; - } + public Key[] ToggleKeys; + + public readonly ModType ModType; public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); @@ -153,7 +150,7 @@ namespace osu.Game.Overlays.Mods button.Deselect(); } - protected ModSection() + public ModSection(ModType type) { AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; @@ -168,7 +165,8 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.TopLeft, Anchor = Anchor.TopLeft, Position = new Vector2(0f, 0f), - Font = OsuFont.GetFont(weight: FontWeight.Bold) + Font = OsuFont.GetFont(weight: FontWeight.Bold), + Text = type.Humanize(LetterCasing.Title) }, ButtonsContainer = new FillFlowContainer { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b93602116b..5775b08ae7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -19,7 +19,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osuTK; @@ -190,13 +189,31 @@ namespace osu.Game.Overlays.Mods Width = content_width, LayoutDuration = 200, LayoutEasing = Easing.OutQuint, - Children = new ModSection[] + Children = new[] { - new DifficultyReductionSection { Action = modButtonPressed }, - new DifficultyIncreaseSection { Action = modButtonPressed }, - new AutomationSection { Action = modButtonPressed }, - new ConversionSection { Action = modButtonPressed }, - new FunSection { Action = modButtonPressed }, + CreateModSection(ModType.DifficultyReduction).With(s => + { + s.ToggleKeys = new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.DifficultyIncrease).With(s => + { + s.ToggleKeys = new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Automation).With(s => + { + s.ToggleKeys = new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Conversion).With(s => + { + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Fun).With(s => + { + s.Action = modButtonPressed; + }), } }, } @@ -455,6 +472,8 @@ namespace osu.Game.Overlays.Mods private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); + protected virtual ModSection CreateModSection(ModType type) => new ModSection(type); + #region Disposal protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/Mods/Sections/AutomationSection.cs b/osu.Game/Overlays/Mods/Sections/AutomationSection.cs deleted file mode 100644 index a2d7fec15f..0000000000 --- a/osu.Game/Overlays/Mods/Sections/AutomationSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class AutomationSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; - public override ModType ModType => ModType.Automation; - - public AutomationSection() - { - Header = @"Automation"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/ConversionSection.cs b/osu.Game/Overlays/Mods/Sections/ConversionSection.cs deleted file mode 100644 index 24fd8c30dd..0000000000 --- a/osu.Game/Overlays/Mods/Sections/ConversionSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class ConversionSection : ModSection - { - protected override Key[] ToggleKeys => null; - public override ModType ModType => ModType.Conversion; - - public ConversionSection() - { - Header = @"Conversion"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs deleted file mode 100644 index 0b7ccd1f25..0000000000 --- a/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class DifficultyIncreaseSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; - public override ModType ModType => ModType.DifficultyIncrease; - - public DifficultyIncreaseSection() - { - Header = @"Difficulty Increase"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs deleted file mode 100644 index 508e92508b..0000000000 --- a/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class DifficultyReductionSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; - public override ModType ModType => ModType.DifficultyReduction; - - public DifficultyReductionSection() - { - Header = @"Difficulty Reduction"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/FunSection.cs b/osu.Game/Overlays/Mods/Sections/FunSection.cs deleted file mode 100644 index af1f5836b1..0000000000 --- a/osu.Game/Overlays/Mods/Sections/FunSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class FunSection : ModSection - { - protected override Key[] ToggleKeys => null; - public override ModType ModType => ModType.Fun; - - public FunSection() - { - Header = @"Fun"; - } - } -} From 0ff300628eff78d2d4c983d029137425b3209628 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 20:07:22 +0900 Subject: [PATCH 215/385] Fix type not being set --- osu.Game/Overlays/Mods/ModSection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index d70013602e..df4d05daad 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -152,6 +152,8 @@ namespace osu.Game.Overlays.Mods public ModSection(ModType type) { + ModType = type; + AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; From 91d34d86f74ba1466323c3ee5f29c6b9541f1640 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:02:23 +0900 Subject: [PATCH 216/385] Abstractify ModSelectOverlay --- .../TestSceneModSelectOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 23 ++++++------------ .../Overlays/Mods/SoloModSelectOverlay.cs | 24 +++++++++++++++++++ osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 5 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Overlays/Mods/SoloModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index bd4010a7f3..b03512ffde 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -265,7 +265,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void checkLabelColor(Func getColour) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == getColour()); - private class TestModSelectOverlay : ModSelectOverlay + private class TestModSelectOverlay : SoloModSelectOverlay { public new Bindable> SelectedMods => base.SelectedMods; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 5775b08ae7..5709ca3b8d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,7 +27,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectOverlay : WaveOverlayContainer + public abstract class ModSelectOverlay : WaveOverlayContainer { private readonly Func isValidMod; public const float HEIGHT = 510; @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - public ModSelectOverlay(Func isValidMod = null) + protected ModSelectOverlay(Func isValidMod = null) { this.isValidMod = isValidMod ?? (m => true); @@ -346,19 +346,6 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - /// - /// Deselect one or more mods. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - private void deselectTypes(Type[] modTypes, bool immediate = false) - { - if (modTypes.Length == 0) return; - - foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes, immediate); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -458,7 +445,7 @@ namespace osu.Game.Overlays.Mods { if (State.Value == Visibility.Visible) sampleOn?.Play(); - deselectTypes(selectedMod.IncompatibleMods, true); + OnModSelected(selectedMod); if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show(); } @@ -470,6 +457,10 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } + protected virtual void OnModSelected(Mod mod) + { + } + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); protected virtual ModSection CreateModSection(ModType type) => new ModSection(type); diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs new file mode 100644 index 0000000000..53d0c9fce9 --- /dev/null +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -0,0 +1,24 @@ +// 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.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class SoloModSelectOverlay : ModSelectOverlay + { + public SoloModSelectOverlay(Func isValidMod = null) + : base(isValidMod) + { + } + + protected override void OnModSelected(Mod mod) + { + base.OnModSelected(mod); + + foreach (var section in ModSectionsContainer.Children) + section.DeselectTypes(mod.IncompatibleMods, true); + } + } +} diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index ed47b5d5ac..280f46f9ab 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Select item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(isValidMod); private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4fca77a176..ff49dd9f7e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -301,7 +301,7 @@ namespace osu.Game.Screens.Select } } - protected virtual ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(); + protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 4019cc38e5e34e8c900bed0e61176fdfa35dbb42 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:03:51 +0900 Subject: [PATCH 217/385] Allow footer buttons to be customised --- osu.Game/Screens/Select/SongSelect.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ff49dd9f7e..4af96b7a29 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -263,9 +263,8 @@ namespace osu.Game.Screens.Select if (Footer != null) { - Footer.AddButton(new FooterButtonMods { Current = Mods }, ModSelect); - Footer.AddButton(new FooterButtonRandom { Action = triggerRandom }); - Footer.AddButton(new FooterButtonOptions(), BeatmapOptions); + foreach (var (button, overlay) in CreateFooterButtons()) + Footer.AddButton(button, overlay); BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show()); BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo)); @@ -301,6 +300,13 @@ namespace osu.Game.Screens.Select } } + protected virtual IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() => new (FooterButton, OverlayContainer)[] + { + (new FooterButtonMods { Current = Mods }, ModSelect), + (new FooterButtonRandom { Action = triggerRandom }, null), + (new FooterButtonOptions(), BeatmapOptions) + }; + protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) From 45e41aaeacf930ce4b5e3b62f85acbc618ce73da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:15:53 +0900 Subject: [PATCH 218/385] Initial implementation of freemod selection overlay --- .../TestSceneFreeModSelectOverlay.cs | 24 +++++ osu.Game/Overlays/Mods/ModButton.cs | 8 +- osu.Game/Overlays/Mods/ModSection.cs | 30 +++--- .../Multiplayer/FreeModSelectOverlay.cs | 101 ++++++++++++++++++ .../Multiplayer/MultiplayerMatchSongSelect.cs | 66 +++++++++++- osu.Game/Screens/Select/Footer.cs | 15 ++- 6 files changed, 216 insertions(+), 28 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs new file mode 100644 index 0000000000..960402df88 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -0,0 +1,24 @@ +// 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.Graphics.Containers; +using osu.Game.Overlays.Mods; +using osu.Game.Screens.OnlinePlay.Multiplayer; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneFreeModSelectOverlay : MultiplayerTestScene + { + private ModSelectOverlay overlay; + + [SetUp] + public new void Setup() => Schedule(() => + { + Child = overlay = new FreeModSelectOverlay + { + State = { Value = Visibility.Visible } + }; + }); + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index ab8efdabcc..9ea4f65eb2 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -174,7 +174,7 @@ namespace osu.Game.Overlays.Mods switch (e.Button) { case MouseButton.Right: - SelectNext(-1); + OnRightClick(e); break; } } @@ -183,10 +183,14 @@ namespace osu.Game.Overlays.Mods protected override bool OnClick(ClickEvent e) { SelectNext(1); - return true; } + protected virtual void OnRightClick(MouseUpEvent e) + { + SelectNext(-1); + } + /// /// Select the next available mod in a specified direction. /// diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index df4d05daad..5de629424b 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods { public class ModSection : Container { - private readonly OsuSpriteText headerLabel; + private readonly Drawable header; public FillFlowContainer ButtonsContainer { get; } @@ -47,10 +47,7 @@ namespace osu.Game.Overlays.Mods if (m == null) return new ModButtonEmpty(); - return new ModButton(m) - { - SelectionChanged = Action, - }; + return CreateModButton(m).With(b => b.SelectionChanged = Action); }).ToArray(); modsLoadCts?.Cancel(); @@ -58,7 +55,7 @@ namespace osu.Game.Overlays.Mods if (modContainers.Length == 0) { ModIconsLoaded = true; - headerLabel.Hide(); + header.Hide(); Hide(); return; } @@ -73,7 +70,7 @@ namespace osu.Game.Overlays.Mods buttons = modContainers.OfType().ToArray(); - headerLabel.FadeIn(200); + header.FadeIn(200); this.FadeIn(200); } } @@ -160,16 +157,9 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.TopCentre; Anchor = Anchor.TopCentre; - Children = new Drawable[] + Children = new[] { - headerLabel = new OsuSpriteText - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Position = new Vector2(0f, 0f), - Font = OsuFont.GetFont(weight: FontWeight.Bold), - Text = type.Humanize(LetterCasing.Title) - }, + header = CreateHeader(type.Humanize(LetterCasing.Title)), ButtonsContainer = new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -185,5 +175,13 @@ namespace osu.Game.Overlays.Mods }, }; } + + protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod); + + protected virtual Drawable CreateHeader(string text) => new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Bold), + Text = text + }; } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs new file mode 100644 index 0000000000..56e74a8460 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs @@ -0,0 +1,101 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer +{ + public class FreeModSelectOverlay : ModSelectOverlay + { + protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); + + private class FreeModSection : ModSection + { + private HeaderCheckbox checkbox; + + public FreeModSection(ModType type) + : base(type) + { + } + + protected override ModButton CreateModButton(Mod mod) => new FreeModButton(mod); + + protected override Drawable CreateHeader(string text) => new Container + { + AutoSizeAxes = Axes.Y, + Width = 175, + Child = checkbox = new HeaderCheckbox + { + LabelText = text, + Changed = onCheckboxChanged + } + }; + + private void onCheckboxChanged(bool value) + { + foreach (var button in ButtonsContainer.OfType()) + { + if (value) + // Note: Buttons where only part of the group has an implementation are not fully supported. + button.SelectAt(0); + else + button.Deselect(); + } + } + + protected override void Update() + { + base.Update(); + + // If any of the buttons aren't selected, deselect the checkbox. + foreach (var button in ButtonsContainer.OfType()) + { + if (button.Mods.Any(m => m.HasImplementation) && !button.Selected) + checkbox.Current.Value = false; + } + } + } + + private class HeaderCheckbox : OsuCheckbox + { + public Action Changed; + + protected override void OnUserChange(bool value) + { + base.OnUserChange(value); + Changed?.Invoke(value); + } + } + + private class FreeModButton : ModButton + { + public FreeModButton(Mod mod) + : base(mod) + { + } + + protected override bool OnClick(ClickEvent e) + { + onClick(); + return true; + } + + protected override void OnRightClick(MouseUpEvent e) => onClick(); + + private void onClick() + { + if (Selected) + Deselect(); + else + SelectNext(1); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index ebc06d2445..5917ed3f49 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -6,17 +6,23 @@ using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; +using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -33,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private StatefulMultiplayerClient client { get; set; } private LoadingLayer loadingLayer; + private FreeModSelectOverlay freeModSelectOverlay; private WorkingBeatmap initialBeatmap; private RulesetInfo initialRuleset; @@ -43,6 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public MultiplayerMatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; + + freeModSelectOverlay = new FreeModSelectOverlay(); } [BackgroundDependencyLoader] @@ -52,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer initialBeatmap = Beatmap.Value; initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); + + FooterPanels.Add(freeModSelectOverlay); } protected override bool OnStart() @@ -111,8 +122,61 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(isValidMod); + + protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() + { + var buttons = base.CreateFooterButtons().ToList(); + buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods(), freeModSelectOverlay)); + return buttons; + } private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } + + public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> + { + public Bindable> Current + { + get => modDisplay.Current; + set => modDisplay.Current = value; + } + + private readonly ModDisplay modDisplay; + + public FooterButtonFreeMods() + { + ButtonContentContainer.Add(modDisplay = new ModDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + DisplayUnrankedText = false, + Scale = new Vector2(0.8f), + ExpansionMode = ExpansionMode.AlwaysContracted, + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + SelectedColour = colours.Yellow; + DeselectedColour = SelectedColour.Opacity(0.5f); + Text = @"freemods"; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateModDisplay(), true); + } + + private void updateModDisplay() + { + if (Current.Value?.Count > 0) + modDisplay.FadeIn(); + else + modDisplay.FadeOut(); + } + } } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 689a11166a..ee13ebda44 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -28,19 +28,16 @@ namespace osu.Game.Screens.Select private readonly List overlays = new List(); - /// THe button to be added. + /// The button to be added. /// The to be toggled by this button. public void AddButton(FooterButton button, OverlayContainer overlay) { - overlays.Add(overlay); - button.Action = () => showOverlay(overlay); + if (overlay != null) + { + overlays.Add(overlay); + button.Action = () => showOverlay(overlay); + } - AddButton(button); - } - - /// Button to be added. - public void AddButton(FooterButton button) - { button.Hovered = updateModeLight; button.HoverLost = updateModeLight; From 4c256f1fb361f4a64d7cf3fa7a919467c56969b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:23:38 +0900 Subject: [PATCH 219/385] Actually populate the playlist item --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 5917ed3f49..4054d84540 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -62,6 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); + freeModSelectOverlay.SelectedMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToList(); FooterPanels.Add(freeModSelectOverlay); } @@ -76,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer item.RequiredMods.Clear(); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); + item.AllowedMods.Clear(); + item.AllowedMods.AddRange(freeModSelectOverlay.SelectedMods.Value.Select(m => m.CreateCopy())); + // If the client is already in a room, update via the client. // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) From c408b46a2158e251d82ce99997d65d2c58c7203f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:25:14 +0900 Subject: [PATCH 220/385] Add AllowedMods to MultiplayerRoomSettings model --- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index 857b38ea60..ad624f18ed 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -25,13 +25,21 @@ namespace osu.Game.Online.Multiplayer [NotNull] public IEnumerable Mods { get; set; } = Enumerable.Empty(); + [NotNull] + public IEnumerable AllowedMods { get; set; } = Enumerable.Empty(); + public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && BeatmapChecksum == other.BeatmapChecksum && Mods.SequenceEqual(other.Mods) + && AllowedMods.SequenceEqual(other.AllowedMods) && RulesetID == other.RulesetID && Name.Equals(other.Name, StringComparison.Ordinal); - public override string ToString() => $"Name:{Name} Beatmap:{BeatmapID} ({BeatmapChecksum}) Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; + public override string ToString() => $"Name:{Name}" + + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" + + $" Mods:{string.Join(',', Mods)}" + + $" AllowedMods:{string.Join(',', AllowedMods)}" + + $" Ruleset:{RulesetID}"; } } From ff8ee379fb9c1684cdd064a32361f27d8fb6105e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:27:31 +0900 Subject: [PATCH 221/385] Fix possible nullref --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 4054d84540..70d3d128dd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using Humanizer; @@ -38,8 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + private readonly FreeModSelectOverlay freeModSelectOverlay; private LoadingLayer loadingLayer; - private FreeModSelectOverlay freeModSelectOverlay; private WorkingBeatmap initialBeatmap; private RulesetInfo initialRuleset; @@ -62,7 +63,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); - freeModSelectOverlay.SelectedMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToList(); + freeModSelectOverlay.SelectedMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); FooterPanels.Add(freeModSelectOverlay); } From b79d1c7b81ef900087608be126f96b6e49e6bdf0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Jan 2021 22:33:03 +0900 Subject: [PATCH 222/385] Add mods to footer --- .../OnlinePlay/Multiplayer/FreeModSelectOverlay.cs | 5 +++++ .../Multiplayer/MultiplayerMatchSongSelect.cs | 11 +++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs index 56e74a8460..10b68ec5a6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs @@ -14,6 +14,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class FreeModSelectOverlay : ModSelectOverlay { + public FreeModSelectOverlay(Func isValidMod = null) + : base(isValidMod) + { + } + protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); private class FreeModSection : ModSection diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 70d3d128dd..86b8f22d34 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private StatefulMultiplayerClient client { get; set; } + private readonly Bindable> freeMods = new Bindable>(Array.Empty()); + private readonly FreeModSelectOverlay freeModSelectOverlay; private LoadingLayer loadingLayer; @@ -52,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectOverlay(); + freeModSelectOverlay = new FreeModSelectOverlay(isValidMod) { SelectedMods = { BindTarget = freeMods } }; } [BackgroundDependencyLoader] @@ -63,7 +65,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); - freeModSelectOverlay.SelectedMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + freeMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + FooterPanels.Add(freeModSelectOverlay); } @@ -79,7 +82,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); item.AllowedMods.Clear(); - item.AllowedMods.AddRange(freeModSelectOverlay.SelectedMods.Value.Select(m => m.CreateCopy())); + item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy())); // If the client is already in a room, update via the client. // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. @@ -132,7 +135,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() { var buttons = base.CreateFooterButtons().ToList(); - buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods(), freeModSelectOverlay)); + buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay)); return buttons; } From 45395cb5e8fcf22d495908f7c77a3b5d90624d98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Jan 2021 23:00:14 +0900 Subject: [PATCH 223/385] 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 9ad5946311..f8ce6befd4 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 3e971d9d4f..97f4320c95 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 4732620085..301e7378a6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 63f057a525e01e3c58ede431293b124c8b14d70d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Jan 2021 20:45:48 +0300 Subject: [PATCH 224/385] Fix dotnet run/publish with runtime specified not working again --- osu.Desktop/osu.Desktop.csproj | 5 +---- osu.Game/osu.Game.csproj | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e201b250d4..cce7907c6c 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,16 +24,13 @@ + - - - - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 97f4320c95..eb541c9de5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,6 +26,7 @@ + From 2c08ce05fa18828248c1fe45aea502059917c0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Jan 2021 22:01:56 +0100 Subject: [PATCH 225/385] Remove game-local enum [Order] attribute In favour of the newly-added framework one. --- .../BeatmapListing/BeatmapSearchFilterRow.cs | 4 +- .../Overlays/BeatmapListing/SearchLanguage.cs | 2 +- .../Overlays/BeatmapSet/Scores/ScoreTable.cs | 4 +- osu.Game/Rulesets/Ruleset.cs | 6 +-- osu.Game/Rulesets/Scoring/HitResult.cs | 2 +- osu.Game/Utils/OrderAttribute.cs | 52 ------------------- 6 files changed, 9 insertions(+), 61 deletions(-) delete mode 100644 osu.Game/Utils/OrderAttribute.cs diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs index b429a5277b..01bcbd3244 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; using Humanizer; -using osu.Game.Utils; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Game.Overlays.BeatmapListing { @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapListing if (typeof(T).IsEnum) { - foreach (var val in OrderAttributeUtils.GetValuesInOrder()) + foreach (var val in EnumExtensions.GetValuesInOrder()) AddItem(val); } } diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs index eee5d8f7e1..015cee8ce3 100644 --- a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs +++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.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.Utils; +using osu.Framework.Utils; namespace osu.Game.Overlays.BeatmapListing { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 324299ccba..ddd1dfa6cd 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -15,7 +16,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Users.Drawables; -using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores var ruleset = scores.First().Ruleset.CreateInstance(); - foreach (var result in OrderAttributeUtils.GetValuesInOrder()) + foreach (var result in EnumExtensions.GetValuesInOrder()) { if (!allScoreStatistics.Contains(result)) continue; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b3b3d11ab3..dbc2bd4d01 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -24,9 +24,9 @@ using osu.Game.Skinning; using osu.Game.Users; using JetBrains.Annotations; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Utils; namespace osu.Game.Rulesets { @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets var validResults = GetValidHitResults(); // enumerate over ordered list to guarantee return order is stable. - foreach (var result in OrderAttributeUtils.GetValuesInOrder()) + foreach (var result in EnumExtensions.GetValuesInOrder()) { switch (result) { @@ -298,7 +298,7 @@ namespace osu.Game.Rulesets /// /// is implicitly included. Special types like are ignored even when specified. /// - protected virtual IEnumerable GetValidHitResults() => OrderAttributeUtils.GetValuesInOrder(); + protected virtual IEnumerable GetValidHitResults() => EnumExtensions.GetValuesInOrder(); /// /// Get a display friendly name for the specified result type. diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 6a3a034fc1..eaa1f95744 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Diagnostics; -using osu.Game.Utils; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Scoring { diff --git a/osu.Game/Utils/OrderAttribute.cs b/osu.Game/Utils/OrderAttribute.cs deleted file mode 100644 index aded7f9814..0000000000 --- a/osu.Game/Utils/OrderAttribute.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace osu.Game.Utils -{ - public static class OrderAttributeUtils - { - /// - /// Get values of an enum in order. Supports custom ordering via . - /// - public static IEnumerable GetValuesInOrder() - { - var type = typeof(T); - - if (!type.IsEnum) - throw new InvalidOperationException("T must be an enum"); - - IEnumerable items = (T[])Enum.GetValues(type); - - if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) == null) - return items; - - return items.OrderBy(i => - { - if (type.GetField(i.ToString()).GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault() is OrderAttribute attr) - return attr.Order; - - throw new ArgumentException($"Not all values of {nameof(T)} have {nameof(OrderAttribute)} specified."); - }); - } - } - - [AttributeUsage(AttributeTargets.Field)] - public class OrderAttribute : Attribute - { - public readonly int Order; - - public OrderAttribute(int order) - { - Order = order; - } - } - - [AttributeUsage(AttributeTargets.Enum)] - public class HasOrderedElementsAttribute : Attribute - { - } -} From 90a82f986bcd2ba59f0ebc3ccc95e942a3e5665a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 16:20:16 +0900 Subject: [PATCH 226/385] Fallback to using json for signalr communication if JIT is unavailable --- .../Online/Multiplayer/MultiplayerClient.cs | 22 +++++++++++++------ .../Spectator/SpectatorStreamingClient.cs | 21 ++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 0d779232d0..c10c4dd1b0 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; @@ -64,13 +66,19 @@ namespace osu.Game.Online.Multiplayer if (connection != null) return; - connection = new HubConnectionBuilder() - .WithUrl(endpoint, options => - { - options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); - }) - .AddMessagePackProtocol() - .Build(); + var builder = new HubConnectionBuilder() + .WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }); + + if (RuntimeInfo.SupportsJIT) + builder.AddMessagePackProtocol(); + else + { + // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. + builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + } + + connection = builder.Build(); // this is kind of SILLY // https://github.com/dotnet/aspnetcore/issues/15198 diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index dac2131035..7a28c179a8 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -115,14 +117,19 @@ namespace osu.Game.Online.Spectator if (connection != null) return; - connection = new HubConnectionBuilder() - .WithUrl(endpoint, options => - { - options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); - }) - .AddMessagePackProtocol() - .Build(); + var builder = new HubConnectionBuilder() + .WithUrl(endpoint, options => { options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); }); + if (RuntimeInfo.SupportsJIT) + builder.AddMessagePackProtocol(); + else + { + // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. + builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + } + + connection = builder.Build(); // until strong typed client support is added, each method must be manually bound (see https://github.com/dotnet/aspnetcore/issues/15198) connection.On(nameof(ISpectatorClient.UserBeganPlaying), ((ISpectatorClient)this).UserBeganPlaying); connection.On(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames); From c3d40440170e98e026bfceaf3dbd25901704e7ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 16:53:56 +0900 Subject: [PATCH 227/385] Avoid using Dapper to fix iOS compatibility of beatmap lookup cache --- ...BeatmapManager_BeatmapOnlineLookupQueue.cs | 32 ++++++++++++------- osu.Game/osu.Game.csproj | 1 - 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index e90ccbb805..ea91f2d2e0 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Dapper; using Microsoft.Data.Sqlite; using osu.Framework.Development; using osu.Framework.IO.Network; @@ -154,20 +153,31 @@ namespace osu.Game.Beatmaps { using (var db = new SqliteConnection(storage.GetDatabaseConnectionString("online"))) { - var found = db.QuerySingleOrDefault( - "SELECT * FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path", beatmap); + db.Open(); - if (found != null) + using (var cmd = db.CreateCommand()) { - var status = (BeatmapSetOnlineStatus)found.approved; + cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; - beatmap.Status = status; - beatmap.BeatmapSet.Status = status; - beatmap.BeatmapSet.OnlineBeatmapSetID = found.beatmapset_id; - beatmap.OnlineBeatmapID = found.beatmap_id; + cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID)); + cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); - LogForModel(set, $"Cached local retrieval for {beatmap}."); - return true; + using (var reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + var status = (BeatmapSetOnlineStatus)reader.GetByte(2); + + beatmap.Status = status; + beatmap.BeatmapSet.Status = status; + beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); + beatmap.OnlineBeatmapID = reader.GetInt32(1); + + LogForModel(set, $"Cached local retrieval for {beatmap}."); + return true; + } + } } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 97f4320c95..bfc5ff302e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,6 @@ - From a616688a47a1d2d1c952eb8fe173cd88f8a64c37 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Jan 2021 23:55:03 +0900 Subject: [PATCH 228/385] 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 f8ce6befd4..7060e88026 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 97f4320c95..a18eef15fc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -27,7 +27,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 301e7378a6..48dc01f5de 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 386f9f78423f205490c817cab468d09a32d12339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Jan 2021 22:36:07 +0100 Subject: [PATCH 229/385] Fix typos in comments --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 2 +- osu.Game/Online/Spectator/SpectatorStreamingClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index c10c4dd1b0..b13d4fa899 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -73,7 +73,7 @@ namespace osu.Game.Online.Multiplayer builder.AddMessagePackProtocol(); else { - // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // eventually we will precompile resolvers for messagepack, but this isn't working currently // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); } diff --git a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs index 7a28c179a8..b95e3f1297 100644 --- a/osu.Game/Online/Spectator/SpectatorStreamingClient.cs +++ b/osu.Game/Online/Spectator/SpectatorStreamingClient.cs @@ -124,7 +124,7 @@ namespace osu.Game.Online.Spectator builder.AddMessagePackProtocol(); else { - // eventuall we will precompile resolvers for messagepack, but this isn't working currently + // eventually we will precompile resolvers for messagepack, but this isn't working currently // see https://github.com/neuecc/MessagePack-CSharp/issues/780#issuecomment-768794308. builder.AddNewtonsoftJsonProtocol(options => { options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); } From da4c207a73b4beb25a59237a2e43ce674ed9ec99 Mon Sep 17 00:00:00 2001 From: Corentin PALLARD Date: Fri, 29 Jan 2021 02:53:26 +0100 Subject: [PATCH 230/385] Fix the ctb auto mod speedup in some occasions --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 32e8ab5da7..ae6868aea5 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.Catch.Replays float positionChange = Math.Abs(lastPosition - h.EffectiveX); double timeAvailable = h.StartTime - lastTime; + if (timeAvailable < 0) + return; + // So we can either make it there without a dash or not. // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) // The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour. From d168de0ae32f0877d225efc13942b96a57a9bba2 Mon Sep 17 00:00:00 2001 From: Corentin PALLARD Date: Fri, 29 Jan 2021 03:03:23 +0100 Subject: [PATCH 231/385] Formatting --- osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index ae6868aea5..64ded8e94f 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -46,7 +46,9 @@ namespace osu.Game.Rulesets.Catch.Replays double timeAvailable = h.StartTime - lastTime; if (timeAvailable < 0) + { return; + } // So we can either make it there without a dash or not. // If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too) From 449f883be15956273c5271eef5236dbd95776792 Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Fri, 29 Jan 2021 11:48:51 +0600 Subject: [PATCH 232/385] add SV multiplier adjustment to TaikoModDifficultyAdjust --- .../Mods/TaikoModDifficultyAdjust.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 56a73ad7df..1d1773fcbb 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -1,11 +1,45 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDifficultyAdjust : ModDifficultyAdjust { + [SettingSource("Slider Velocity", "Adjust a beatmap's set SV", LAST_SETTING_ORDER + 1)] + public BindableNumber SliderVelocity { get; } = new BindableFloat + { + Precision = 0.05f, + MinValue = 0.25f, + MaxValue = 4, + Default = 1, + Value = 1, + }; + + public override string SettingDescription + { + get + { + string sliderVelocity = SliderVelocity.IsDefault ? string.Empty : $"SV {SliderVelocity.Value:N1}"; + + return string.Join(", ", new[] + { + base.SettingDescription, + sliderVelocity + }.Where(s => !string.IsNullOrEmpty(s))); + } + } + + protected override void ApplySettings(BeatmapDifficulty difficulty) + { + base.ApplySettings(difficulty); + + ApplySetting(SliderVelocity, sv => difficulty.SliderMultiplier *= sv); + } } } From a61444690ea8324719e4f04f1cb6e02b3706bb19 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 16:32:28 +0900 Subject: [PATCH 233/385] Remove all usage of CatchUnobservedExceptions This should no longer be required with the recent framework side change that stops a game from crashing on unobserved exceptions (https://github.com/ppy/osu-framework/pull/4171). --- osu.Game/Extensions/TaskExtensions.cs | 36 ------------------- .../Multiplayer/StatefulMultiplayerClient.cs | 3 +- .../OnlinePlay/Multiplayer/Multiplayer.cs | 3 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 8 +---- .../Multiplayer/MultiplayerRoomManager.cs | 3 +- .../Participants/ParticipantPanel.cs | 3 +- 6 files changed, 5 insertions(+), 51 deletions(-) delete mode 100644 osu.Game/Extensions/TaskExtensions.cs diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs deleted file mode 100644 index 4138c2757a..0000000000 --- a/osu.Game/Extensions/TaskExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable enable - -using System; -using System.Threading.Tasks; -using osu.Framework.Extensions.ExceptionExtensions; -using osu.Framework.Logging; - -namespace osu.Game.Extensions -{ - public static class TaskExtensions - { - /// - /// Denote a task which is to be run without local error handling logic, where failure is not catastrophic. - /// Avoids unobserved exceptions from being fired. - /// - /// The task. - /// - /// Whether errors should be logged as errors visible to users, or as debug messages. - /// Logging as debug will essentially silence the errors on non-release builds. - /// - public static void CatchUnobservedExceptions(this Task task, bool logAsError = false) - { - task.ContinueWith(t => - { - Exception? exception = t.Exception?.AsSingular(); - if (logAsError) - Logger.Error(exception, $"Error running task: {exception?.Message ?? "(unknown)"}", LoggingTarget.Runtime, true); - else - Logger.Log($"Error running task: {exception}", LoggingTarget.Runtime, LogLevel.Debug); - }, TaskContinuationOptions.NotOnRanToCompletion); - } - } -} diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index f0e11b2b8b..48194d1f0f 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -104,7 +103,7 @@ namespace osu.Game.Online.Multiplayer if (!connected.NewValue && Room != null) { Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important); - LeaveRoom().CatchUnobservedExceptions(); + LeaveRoom(); } }); } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index 76f5c74433..ae22e1fcec 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; @@ -23,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.OnResuming(last); if (client.Room != null) - client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true); + client.ChangeState(MultiplayerUserState.Idle); } protected override void UpdatePollingRate(bool isIdle) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7c4b6d18ec..c071637b9b 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; @@ -237,7 +236,6 @@ 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(); } @@ -248,11 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } client.ToggleReady() - .ContinueWith(t => - { - t.CatchUnobservedExceptions(true); // will run immediately. - endOperation(); - }); + .ContinueWith(t => endOperation()); void endOperation() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs index 61d8896732..65d112a032 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Logging; -using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; @@ -69,7 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.PartRoom(); - multiplayerClient.LeaveRoom().CatchUnobservedExceptions(); + multiplayerClient.LeaveRoom(); // Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case. // This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling. diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index f99655e305..b5533f49cc 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -176,7 +175,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants if (Room.Host?.UserID != api.LocalUser.Value.Id) return; - Client.TransferHost(targetUser).CatchUnobservedExceptions(true); + Client.TransferHost(targetUser); }) }; } From 37ef5c70729c6f99cf51cc6841eb4833a728e51c Mon Sep 17 00:00:00 2001 From: Firmatorenio Date: Fri, 29 Jan 2021 15:04:55 +0600 Subject: [PATCH 234/385] rename SliderVelocity to ScrollSpeed --- .../Mods/TaikoModDifficultyAdjust.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 1d1773fcbb..4006652bd5 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Slider Velocity", "Adjust a beatmap's set SV", LAST_SETTING_ORDER + 1)] - public BindableNumber SliderVelocity { get; } = new BindableFloat + [SettingSource("Scroll Speed", "Adjust a beatmap's set scroll speed", LAST_SETTING_ORDER + 1)] + public BindableNumber ScrollSpeed { get; } = new BindableFloat { Precision = 0.05f, MinValue = 0.25f, @@ -25,12 +25,12 @@ namespace osu.Game.Rulesets.Taiko.Mods { get { - string sliderVelocity = SliderVelocity.IsDefault ? string.Empty : $"SV {SliderVelocity.Value:N1}"; + string scrollSpeed = ScrollSpeed.IsDefault ? string.Empty : $"Scroll x{ScrollSpeed.Value:N1}"; return string.Join(", ", new[] { base.SettingDescription, - sliderVelocity + scrollSpeed }.Where(s => !string.IsNullOrEmpty(s))); } } @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - ApplySetting(SliderVelocity, sv => difficulty.SliderMultiplier *= sv); + ApplySetting(ScrollSpeed, scroll => difficulty.SliderMultiplier *= scroll); } } } From ab9a3e6dd05bd4a85ab5d1be0e7acc726148e029 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 29 Jan 2021 18:21:22 +0900 Subject: [PATCH 235/385] Pass allowed mods and consume on server callback --- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index f0e11b2b8b..e5b07ddfb4 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -192,7 +192,8 @@ namespace osu.Game.Online.Multiplayer BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, - Mods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods + Mods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods, + AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods }); } @@ -502,6 +503,7 @@ namespace osu.Game.Online.Multiplayer var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance(); var mods = settings.Mods.Select(m => m.ToMod(ruleset)); + var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); PlaylistItem playlistItem = new PlaylistItem { @@ -511,6 +513,7 @@ namespace osu.Game.Online.Multiplayer }; playlistItem.RequiredMods.AddRange(mods); + playlistItem.AllowedMods.AddRange(allowedMods); apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity. apiRoom.Playlist.Add(playlistItem); From 18e6afbec06a2111275ba6415eb7d132f52f9ce2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 19:16:10 +0900 Subject: [PATCH 236/385] Ensure the item is present before trying to select it --- .../Overlays/Settings/Sections/SkinSection.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 56c677d5b5..7c8309fd56 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -121,7 +121,22 @@ namespace osu.Game.Overlays.Settings.Sections }); } - private void updateSelectedSkinFromConfig() => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == configBindable.Value); + private void updateSelectedSkinFromConfig() + { + int id = configBindable.Value; + + var skin = skinDropdown.Items.FirstOrDefault(s => s.ID == id); + + if (skin == null) + { + // there may be a thread race condition where an item is selected that hasn't yet been added to the dropdown. + // to avoid adding complexity, let's just ensure the item is added so we can perform the selection. + skin = skins.Query(s => s.ID == id); + addItem(skin); + } + + dropdownBindable.Value = skin; + } private void updateItems() { @@ -134,14 +149,14 @@ namespace osu.Game.Overlays.Settings.Sections private void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); - sortUserSkins(newDropdownItems); - skinDropdown.Items = newDropdownItems; - }); - } + Schedule(() => addItem(item)); + } + + private void addItem(SkinInfo item) + { + List newDropdownItems = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToList(); + sortUserSkins(newDropdownItems); + skinDropdown.Items = newDropdownItems; } private void itemRemoved(ValueChangedEvent> weakItem) From 16f3d1815f27a344ca2e9a662f960f97ef4db666 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 19:53:56 +0900 Subject: [PATCH 237/385] Fix SQLite exception thrown is a beatmap lookup is attempted without an OnlineBeatmapID present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out the SQLite API isn't smart enough to handle nullables directly, so we need to help it out a bit. Stops the following from being thrown: ``` System.InvalidOperationException: Value must be set. at Microsoft.Data.Sqlite.SqliteParameter.Bind(sqlite3_stmt stmt) = 3 at at Microsoft.Data.Sqlite.SqliteParameterCollection.Bind(sqlite3_stmt stmt) = 3 at at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader() at osu.Game.Beatmaps.BeatmapManager.BeatmapOnlineLookupQueue.checkLocalCache(BeatmapSetInfo set, BeatmapInfo beatmap) in /Users/dean/Projects/osu/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs:line 166 = 166 ``` --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index ea91f2d2e0..7c4b344c9e 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); - cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID)); + cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); cmd.Parameters.Add(new SqliteParameter("@Path", beatmap.Path)); using (var reader = cmd.ExecuteReader()) From f25809d35f9fd084f7486bab8066dc0da6b59347 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 19:55:51 +0900 Subject: [PATCH 238/385] Ensure spinners only handle input during their hittable time While this was already being enforced inside of `CheckForResult`, the internal tracking values of rotation were still being incremented as long as the `DrawableSpinner` was present. This resulted in incorrect SPM values being displayed if a user was to start spinning before the object's `StartTime`. Kind of annoying to write a test for (there's no setup for spinners yet) but am willing to do so if that is deemed necessary. Closes https://github.com/ppy/osu/issues/11600. --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 56aedebed3..43012563ae 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Ranking; using osu.Game.Skinning; @@ -242,6 +243,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.Update(); + HandleUserInput = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime; + if (HandleUserInput) RotationTracker.Tracking = !Result.HasResult && (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); @@ -255,6 +258,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!SpmCounter.IsPresent && RotationTracker.Tracking) SpmCounter.FadeIn(HitObject.TimeFadeIn); + SpmCounter.SetRotation(Result.RateAdjustedRotation); updateBonusScore(); From 5a306dfc2b261e5926817eb03352a11ec846c1ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Jan 2021 20:22:25 +0900 Subject: [PATCH 239/385] Fix unused using --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 43012563ae..ab8156f4f2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -18,7 +18,6 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Ranking; using osu.Game.Skinning; From d521bfc251195a4eb5560d9685181bf079602d46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 Jan 2021 02:35:11 +0900 Subject: [PATCH 240/385] Don't directly update HandleUserInput (as it is used by mods) --- .../Objects/Drawables/DrawableSpinner.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index ab8156f4f2..c58f703bef 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -242,10 +242,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.Update(); - HandleUserInput = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime; - if (HandleUserInput) - RotationTracker.Tracking = !Result.HasResult && (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + { + bool isValidSpinningTime = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime; + bool correctButtonPressed = (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + + RotationTracker.Tracking = !Result.HasResult + && correctButtonPressed + && isValidSpinningTime; + } if (spinningSample != null && spinnerFrequencyModulate) spinningSample.Frequency.Value = spinning_sample_modulated_base_frequency + Progress; From ae08ef25437efd3e6aa6661a8d1d627cff338a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Jan 2021 20:32:45 +0100 Subject: [PATCH 241/385] Reset SPM counter state on DHO application --- .../Skinning/Default/SpinnerSpmCounter.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs index e5952ecf97..69355f624b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs @@ -4,16 +4,21 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Skinning.Default { public class SpinnerSpmCounter : Container { + [Resolved] + private DrawableHitObject drawableSpinner { get; set; } + private readonly OsuSpriteText spmText; public SpinnerSpmCounter() @@ -38,6 +43,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default }; } + protected override void LoadComplete() + { + base.LoadComplete(); + drawableSpinner.HitObjectApplied += resetState; + } + private double spm; public double SpinsPerMinute @@ -82,5 +93,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current }); } + + private void resetState(DrawableHitObject hitObject) + { + SpinsPerMinute = 0; + records.Clear(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableSpinner != null) + drawableSpinner.HitObjectApplied -= resetState; + } } } From 0aaa62efc2d9f2d1e8f6d46632661b0e6ef8db03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Jan 2021 20:55:56 +0100 Subject: [PATCH 242/385] Add failing test case --- .../NonVisual/OngoingOperationTrackerTest.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs index eef9582af9..10216c3339 100644 --- a/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs +++ b/osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs @@ -3,8 +3,12 @@ using System; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Framework.Testing; +using osu.Game.Screens; using osu.Game.Screens.OnlinePlay; using osu.Game.Tests.Visual; @@ -58,5 +62,45 @@ namespace osu.Game.Tests.NonVisual AddStep("end operation", () => operation.Dispose()); AddAssert("operation is ended", () => !operationInProgress.Value); } + + [Test] + public void TestOperationDisposalAfterScreenExit() + { + TestScreenWithTracker screen = null; + OsuScreenStack stack; + IDisposable operation = null; + + AddStep("create screen with tracker", () => + { + Child = stack = new OsuScreenStack + { + RelativeSizeAxes = Axes.Both + }; + + stack.Push(screen = new TestScreenWithTracker()); + }); + AddUntilStep("wait for loaded", () => screen.IsLoaded); + + AddStep("begin operation", () => operation = screen.OngoingOperationTracker.BeginOperation()); + AddAssert("operation in progress", () => screen.OngoingOperationTracker.InProgress.Value); + + AddStep("dispose after screen exit", () => + { + screen.Exit(); + operation.Dispose(); + }); + AddAssert("operation ended", () => !screen.OngoingOperationTracker.InProgress.Value); + } + + private class TestScreenWithTracker : OsuScreen + { + public OngoingOperationTracker OngoingOperationTracker { get; private set; } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = OngoingOperationTracker = new OngoingOperationTracker(); + } + } } } From 96f56d1c942505797f20cf1c166b5452c60ea592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Jan 2021 21:00:13 +0100 Subject: [PATCH 243/385] Return tracker lease via UnbindAll() Improves reliability by being fail-safe in case of multiple returns, which can happen if the operation tracker is part of a screen being exited (as is the case with its current primary usage in multiplayer). --- .../Screens/OnlinePlay/OngoingOperationTracker.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index b7ee84eb9e..9e88fabb3d 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -53,20 +53,15 @@ namespace osu.Game.Screens.OnlinePlay // for extra safety, marshal the end of operation back to the update thread if necessary. Scheduler.Add(() => { - leasedInProgress?.Return(); + // UnbindAll() is purposefully used instead of Return() - the two do roughly the same thing, with one difference: + // the former won't throw if the lease has already been returned before. + // this matters because framework can unbind the lease via the internal UnbindAllBindables(), which is not always detectable + // (it is in the case of disposal, but not in the case of screen exit - at least not cleanly). + leasedInProgress?.UnbindAll(); leasedInProgress = null; }, false); } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - // base call does an UnbindAllBindables(). - // clean up the leased reference here so that it doesn't get returned twice. - leasedInProgress = null; - } - private class OngoingOperation : IDisposable { private readonly OngoingOperationTracker tracker; From 5f320cd4264ad444fe5cb3f7aa7bef74d530e932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Jan 2021 21:03:09 +0100 Subject: [PATCH 244/385] Move lease check inside schedule Theoretically safer due to avoiding a potential data race (change in `leasedInProgress` between the time of the check and start of schedule execution). --- osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs index 9e88fabb3d..aabeafe460 100644 --- a/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs +++ b/osu.Game/Screens/OnlinePlay/OngoingOperationTracker.cs @@ -47,12 +47,12 @@ namespace osu.Game.Screens.OnlinePlay private void endOperationWithKnownLease(LeasedBindable lease) { - if (lease != leasedInProgress) - return; - // for extra safety, marshal the end of operation back to the update thread if necessary. Scheduler.Add(() => { + if (lease != leasedInProgress) + return; + // UnbindAll() is purposefully used instead of Return() - the two do roughly the same thing, with one difference: // the former won't throw if the lease has already been returned before. // this matters because framework can unbind the lease via the internal UnbindAllBindables(), which is not always detectable From d7e5a212134e371838ee342ecb476bdcc0347c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Jan 2021 15:23:53 +0100 Subject: [PATCH 245/385] Add failing test case --- .../Formats/LegacyStoryboardDecoderTest.cs | 20 +++++++++++++++++++ osu.Game.Tests/Resources/animation-types.osb | 9 +++++++++ 2 files changed, 29 insertions(+) create mode 100644 osu.Game.Tests/Resources/animation-types.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 7bee580863..bcde899789 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -129,5 +129,25 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(3456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X); } } + + [Test] + public void TestDecodeOutOfRangeLoopAnimationType() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("animation-types.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0); + Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[0]).LoopType); + Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[1]).LoopType); + Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[2]).LoopType); + Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[3]).LoopType); + Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[4]).LoopType); + Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType); + } + } } } diff --git a/osu.Game.Tests/Resources/animation-types.osb b/osu.Game.Tests/Resources/animation-types.osb new file mode 100644 index 0000000000..82233b7d30 --- /dev/null +++ b/osu.Game.Tests/Resources/animation-types.osb @@ -0,0 +1,9 @@ +osu file format v14 + +[Events] +Animation,Foreground,Centre,"forever-string.png",330,240,10,108,LoopForever +Animation,Foreground,Centre,"once-string.png",330,240,10,108,LoopOnce +Animation,Foreground,Centre,"forever-number.png",330,240,10,108,0 +Animation,Foreground,Centre,"once-number.png",330,240,10,108,1 +Animation,Foreground,Centre,"undefined-number.png",330,240,10,108,16 +Animation,Foreground,Centre,"omitted.png",330,240,10,108 From b9a49d5589dfd91e651ad84c0872401e27ab192e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Jan 2021 15:33:07 +0100 Subject: [PATCH 246/385] Coerce undefined animation loop types to Forever --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 9a244c8bb2..b9bf6823b5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -139,7 +139,7 @@ namespace osu.Game.Beatmaps.Formats // this is random as hell but taken straight from osu-stable. frameDelay = Math.Round(0.015 * frameDelay) * 1.186 * (1000 / 60f); - var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; + var loopType = split.Length > 8 ? parseAnimationLoopType(split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); storyboard.GetLayer(layer).Add(storyboardSprite); break; @@ -341,6 +341,12 @@ namespace osu.Game.Beatmaps.Formats } } + private AnimationLoopType parseAnimationLoopType(string value) + { + var parsed = (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), value); + return Enum.IsDefined(typeof(AnimationLoopType), parsed) ? parsed : AnimationLoopType.LoopForever; + } + private void handleVariables(string line) { var pair = SplitKeyVal(line, '='); From 81b052b866af6422434f96638dabace022618f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Jan 2021 20:18:12 +0100 Subject: [PATCH 247/385] Add failing test cases --- .../Rulesets/Mods/ModTimeRampTest.cs | 82 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModTimeRamp.cs | 4 +- 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs diff --git a/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs new file mode 100644 index 0000000000..894648b8b3 --- /dev/null +++ b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs @@ -0,0 +1,82 @@ +// 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.Audio.Track; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Tests.Rulesets.Mods +{ + [TestFixture] + public class ModTimeRampTest + { + private const double start_time = 1000; + private const double duration = 9000; + + private TrackVirtual track; + private IBeatmap beatmap; + + [SetUp] + public void SetUp() + { + track = new TrackVirtual(20_000); + beatmap = new Beatmap + { + HitObjects = + { + new Spinner + { + StartTime = start_time, + Duration = duration + } + } + }; + } + + [TestCase(0, 1)] + [TestCase(start_time, 1)] + [TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS / 2, 1.25)] + [TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS, 1.5)] + [TestCase(start_time + duration, 1.5)] + [TestCase(15000, 1.5)] + public void TestModWindUp(double time, double expectedRate) + { + var mod = new ModWindUp(); + mod.ApplyToBeatmap(beatmap); + mod.ApplyToTrack(track); + + seekTrackAndUpdateMod(mod, time); + + Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate)); + } + + [TestCase(0, 1)] + [TestCase(start_time, 1)] + [TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS / 2, 0.75)] + [TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS, 0.5)] + [TestCase(start_time + duration, 0.5)] + [TestCase(15000, 0.5)] + public void TestModWindDown(double time, double expectedRate) + { + var mod = new ModWindDown + { + FinalRate = { Value = 0.5 } + }; + mod.ApplyToBeatmap(beatmap); + mod.ApplyToTrack(track); + + seekTrackAndUpdateMod(mod, time); + + Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate)); + } + + private void seekTrackAndUpdateMod(ModTimeRamp mod, double time) + { + track.Seek(time); + // update the mod via a fake playfield to re-calculate the current rate. + mod.Update(null); + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 4d43ae73d3..c691339a81 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mods /// /// The point in the beatmap at which the final ramping rate should be reached. /// - private const double final_rate_progress = 0.75f; + public const double FINAL_RATE_PROGRESS = 0.75f; [SettingSource("Initial rate", "The starting speed of the track")] public abstract BindableNumber InitialRate { get; } @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mods SpeedChange.SetDefault(); beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0; - finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0); + finalRateTime = FINAL_RATE_PROGRESS * (lastObject?.GetEndTime() ?? 0); } public virtual void Update(Playfield playfield) From 547b3d8bed86d1398da54ea3d6ffcca8f883763b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Jan 2021 20:34:56 +0100 Subject: [PATCH 248/385] Fix speed change calculation in time ramp mods --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index c691339a81..a28a0351a6 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -66,17 +66,18 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToBeatmap(IBeatmap beatmap) { - HitObject lastObject = beatmap.HitObjects.LastOrDefault(); - SpeedChange.SetDefault(); - beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0; - finalRateTime = FINAL_RATE_PROGRESS * (lastObject?.GetEndTime() ?? 0); + double firstObjectStart = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0; + double lastObjectEnd = beatmap.HitObjects.LastOrDefault()?.GetEndTime() ?? 0; + + beginRampTime = firstObjectStart; + finalRateTime = firstObjectStart + FINAL_RATE_PROGRESS * (lastObjectEnd - firstObjectStart); } public virtual void Update(Playfield playfield) { - applyRateAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); + applyRateAdjustment((track.CurrentTime - beginRampTime) / (finalRateTime - beginRampTime)); } /// From a0de1cbfd07447789c4093d67c10e782e6463725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Jan 2021 21:09:41 +0100 Subject: [PATCH 249/385] Handle no-duration single-object edge case --- .../Rulesets/Mods/ModTimeRampTest.cs | 55 +++++++++++++++---- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs index 894648b8b3..4b9f2181dc 100644 --- a/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs +++ b/osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs @@ -16,23 +16,11 @@ namespace osu.Game.Tests.Rulesets.Mods private const double duration = 9000; private TrackVirtual track; - private IBeatmap beatmap; [SetUp] public void SetUp() { track = new TrackVirtual(20_000); - beatmap = new Beatmap - { - HitObjects = - { - new Spinner - { - StartTime = start_time, - Duration = duration - } - } - }; } [TestCase(0, 1)] @@ -43,6 +31,7 @@ namespace osu.Game.Tests.Rulesets.Mods [TestCase(15000, 1.5)] public void TestModWindUp(double time, double expectedRate) { + var beatmap = createSingleSpinnerBeatmap(); var mod = new ModWindUp(); mod.ApplyToBeatmap(beatmap); mod.ApplyToTrack(track); @@ -60,6 +49,7 @@ namespace osu.Game.Tests.Rulesets.Mods [TestCase(15000, 0.5)] public void TestModWindDown(double time, double expectedRate) { + var beatmap = createSingleSpinnerBeatmap(); var mod = new ModWindDown { FinalRate = { Value = 0.5 } @@ -72,11 +62,52 @@ namespace osu.Game.Tests.Rulesets.Mods Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate)); } + [TestCase(0, 1)] + [TestCase(start_time, 1)] + [TestCase(2 * start_time, 1.5)] + public void TestZeroDurationMap(double time, double expectedRate) + { + var beatmap = createSingleObjectBeatmap(); + var mod = new ModWindUp(); + mod.ApplyToBeatmap(beatmap); + mod.ApplyToTrack(track); + + seekTrackAndUpdateMod(mod, time); + + Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate)); + } + private void seekTrackAndUpdateMod(ModTimeRamp mod, double time) { track.Seek(time); // update the mod via a fake playfield to re-calculate the current rate. mod.Update(null); } + + private static Beatmap createSingleSpinnerBeatmap() + { + return new Beatmap + { + HitObjects = + { + new Spinner + { + StartTime = start_time, + Duration = duration + } + } + }; + } + + private static Beatmap createSingleObjectBeatmap() + { + return new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = start_time } + } + }; + } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index a28a0351a6..b6916c838e 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - applyRateAdjustment((track.CurrentTime - beginRampTime) / (finalRateTime - beginRampTime)); + applyRateAdjustment((track.CurrentTime - beginRampTime) / Math.Max(1, finalRateTime - beginRampTime)); } /// From 39d46d21e6e2453121e513a2025cd6c5a2bd9ba9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Jan 2021 23:44:27 +0300 Subject: [PATCH 250/385] Add failing test case --- .../Online/TestSceneStandAloneChatDisplay.cs | 116 ++++++++++++------ 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 492abdd88d..02ef024128 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -8,6 +8,7 @@ using osu.Game.Users; using osuTK; using System; using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Chat; @@ -16,8 +17,6 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneStandAloneChatDisplay : OsuTestScene { - private readonly Channel testChannel = new Channel(); - private readonly User admin = new User { Username = "HappyStick", @@ -46,78 +45,84 @@ namespace osu.Game.Tests.Visual.Online [Cached] private ChannelManager channelManager = new ChannelManager(); - private readonly TestStandAloneChatDisplay chatDisplay; - private readonly TestStandAloneChatDisplay chatDisplay2; + private TestStandAloneChatDisplay chatDisplay; + private TestStandAloneChatDisplay chatDisplay2; + private int messageIdSequence; + + private Channel testChannel; public TestSceneStandAloneChatDisplay() { Add(channelManager); - - Add(chatDisplay = new TestStandAloneChatDisplay - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding(20), - Size = new Vector2(400, 80) - }); - - Add(chatDisplay2 = new TestStandAloneChatDisplay(true) - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding(20), - Size = new Vector2(400, 150) - }); } - protected override void LoadComplete() + [SetUp] + public void SetUp() => Schedule(() => { - base.LoadComplete(); + messageIdSequence = 0; + channelManager.CurrentChannel.Value = testChannel = new Channel(); - channelManager.CurrentChannel.Value = testChannel; + Children = new[] + { + chatDisplay = new TestStandAloneChatDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding(20), + Size = new Vector2(400, 80), + Channel = { Value = testChannel }, + }, + chatDisplay2 = new TestStandAloneChatDisplay(true) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding(20), + Size = new Vector2(400, 150), + Channel = { Value = testChannel }, + } + }; + }); - chatDisplay.Channel.Value = testChannel; - chatDisplay2.Channel.Value = testChannel; - - int sequence = 0; - - AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++) + [Test] + public void TestManyMessages() + { + AddStep("message from admin", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = admin, Content = "I am a wang!" })); - AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message from team red", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = redUser, Content = "I am team red." })); - AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message from team red", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = redUser, Content = "I plan to win!" })); - AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = blueUser, Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand." })); - AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message from admin", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = admin, Content = "Okay okay, calm down guys. Let's do this!" })); - AddStep("message from long username", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message from long username", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = longUsernameUser, Content = "Hi guys, my new username is lit!" })); - AddStep("message with new date", () => testChannel.AddNewMessages(new Message(sequence++) + AddStep("message with new date", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = longUsernameUser, Content = "Message from the future!", @@ -131,7 +136,7 @@ namespace osu.Game.Tests.Visual.Online { for (int i = 0; i < messages_per_call; i++) { - testChannel.AddNewMessages(new Message(sequence++) + testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = longUsernameUser, Content = "Many messages! " + Guid.NewGuid(), @@ -156,6 +161,45 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); } + /// + /// Tests that when a message gets wrapped by the chat display getting contracted while scrolled to bottom, the chat will still keep scrolling down. + /// + [Test] + public void TestMessageWrappingKeepsAutoScrolling() + { + AddStep("fill chat", () => + { + for (int i = 0; i < 10; i++) + { + testChannel.AddNewMessages(new Message(messageIdSequence++) + { + Sender = longUsernameUser, + Content = $"some stuff {Guid.NewGuid()}", + }); + } + }); + + AddAssert("ensure scrolled to bottom", () => chatDisplay.ScrolledToBottom); + + // send message with short words for text wrapping to occur when contracting chat. + AddStep("send lorem ipsum", () => testChannel.AddNewMessages(new Message(messageIdSequence++) + { + Sender = longUsernameUser, + Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce et bibendum velit.", + })); + + AddStep("contract chat", () => chatDisplay.Width -= 100); + AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + + AddStep("send another message", () => testChannel.AddNewMessages(new Message(messageIdSequence++) + { + Sender = admin, + Content = "As we were saying...", + })); + + AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + } + private class TestStandAloneChatDisplay : StandAloneChatDisplay { public TestStandAloneChatDisplay(bool textbox = false) From e806e5bcd1580f1c85026e53d289d7cf933e4767 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Jan 2021 23:37:52 +0300 Subject: [PATCH 251/385] Improve robustness of chat auto-scrolling logic Fix auto-scrolling state changing by old messages removal logic --- osu.Game/Overlays/Chat/DrawableChannel.cs | 47 ++++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 5926d11c03..f1aa387c17 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Chat { public readonly Channel Channel; protected FillFlowContainer ChatLineFlow; - private OsuScrollContainer scroll; + private ChannelScrollContainer scroll; private bool scrollbarVisible = true; @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.Both, Masking = true, - Child = scroll = new OsuScrollContainer + Child = scroll = new ChannelScrollContainer { ScrollbarVisible = scrollbarVisible, RelativeSizeAxes = Axes.Both, @@ -80,12 +80,6 @@ namespace osu.Game.Overlays.Chat Channel.PendingMessageResolved += pendingMessageResolved; } - protected override void LoadComplete() - { - base.LoadComplete(); - scrollToEnd(); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -113,8 +107,6 @@ namespace osu.Game.Overlays.Chat ChatLineFlow.Clear(); } - bool shouldScrollToEnd = scroll.IsScrolledToEnd(10) || !chatLines.Any() || newMessages.Any(m => m is LocalMessage); - // Add up to last Channel.MAX_HISTORY messages var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY)); @@ -153,8 +145,10 @@ namespace osu.Game.Overlays.Chat } } - if (shouldScrollToEnd) - scrollToEnd(); + // due to the scroll adjusts from old messages removal above, a scroll-to-end must be enforced, + // to avoid making the container think the user has scrolled back up and unwantedly disable auto-scrolling. + if (scroll.ShouldAutoScroll || newMessages.Any(m => m is LocalMessage)) + ScheduleAfterChildren(() => scroll.ScrollToEnd()); }); private void pendingMessageResolved(Message existing, Message updated) => Schedule(() => @@ -178,8 +172,6 @@ namespace osu.Game.Overlays.Chat private IEnumerable chatLines => ChatLineFlow.Children.OfType(); - private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); - public class DaySeparator : Container { public float TextSize @@ -243,5 +235,32 @@ namespace osu.Game.Overlays.Chat }; } } + + /// + /// An with functionality to automatically scrolls whenever the maximum scrollable distance increases. + /// + private class ChannelScrollContainer : OsuScrollContainer + { + private const float auto_scroll_leniency = 10f; + + private float? lastExtent; + + /// + /// Whether this should automatically scroll to end on the next call to . + /// + public bool ShouldAutoScroll { get; private set; } = true; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if ((lastExtent == null || ScrollableExtent > lastExtent) && ShouldAutoScroll) + ScrollToEnd(); + else + ShouldAutoScroll = IsScrolledToEnd(auto_scroll_leniency); + + lastExtent = ScrollableExtent; + } + } } } From 230b347c1efbfc7baa7da6af8676356e47d7a4d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 12:18:11 +0900 Subject: [PATCH 252/385] Move ModSelectOverlay.IsValidMod to a property --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 29 ++++++++++++++----- .../Overlays/Mods/SoloModSelectOverlay.cs | 6 ---- .../Multiplayer/FreeModSelectOverlay.cs | 5 ---- .../Multiplayer/MultiplayerMatchSongSelect.cs | 11 +++++-- osu.Game/Screens/Select/MatchSongSelect.cs | 5 +++- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 5709ca3b8d..c21b9ba409 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -29,7 +30,6 @@ namespace osu.Game.Overlays.Mods { public abstract class ModSelectOverlay : WaveOverlayContainer { - private readonly Func isValidMod; public const float HEIGHT = 510; protected readonly TriangleButton DeselectAllButton; @@ -46,6 +46,20 @@ namespace osu.Game.Overlays.Mods protected readonly ModSettingsContainer ModSettingsContainer; + [NotNull] + private Func isValidMod = m => true; + + [NotNull] + public Func IsValidMod + { + get => isValidMod; + set + { + isValidMod = value ?? throw new ArgumentNullException(nameof(value)); + updateAvailableMods(); + } + } + public readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); private Bindable>> availableMods; @@ -60,10 +74,8 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - protected ModSelectOverlay(Func isValidMod = null) + protected ModSelectOverlay() { - this.isValidMod = isValidMod ?? (m => true); - Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); @@ -350,7 +362,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - availableMods.BindValueChanged(availableModsChanged, true); + availableMods.BindValueChanged(_ => updateAvailableMods(), true); SelectedMods.BindValueChanged(selectedModsChanged, true); } @@ -405,12 +417,13 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(GlobalAction action) => false; // handled by back button - private void availableModsChanged(ValueChangedEvent>> mods) + private void updateAvailableMods() { - if (mods.NewValue == null) return; + if (availableMods.Value == null) + return; foreach (var section in ModSectionsContainer.Children) - section.Mods = mods.NewValue[section.ModType].Where(isValidMod); + section.Mods = availableMods.Value[section.ModType].Where(IsValidMod); } private void selectedModsChanged(ValueChangedEvent> mods) diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs index 53d0c9fce9..d039ad1f98 100644 --- a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -1,18 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { public class SoloModSelectOverlay : ModSelectOverlay { - public SoloModSelectOverlay(Func isValidMod = null) - : base(isValidMod) - { - } - protected override void OnModSelected(Mod mod) { base.OnModSelected(mod); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs index 10b68ec5a6..56e74a8460 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs @@ -14,11 +14,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class FreeModSelectOverlay : ModSelectOverlay { - public FreeModSelectOverlay(Func isValidMod = null) - : base(isValidMod) - { - } - protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); private class FreeModSection : ModSection diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 86b8f22d34..d36ebeec0f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -54,7 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectOverlay(isValidMod) { SelectedMods = { BindTarget = freeMods } }; + freeModSelectOverlay = new FreeModSelectOverlay + { + SelectedMods = { BindTarget = freeMods }, + IsValidMod = isValidMod, + }; } [BackgroundDependencyLoader] @@ -130,7 +134,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay + { + IsValidMod = isValidMod + }; protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() { diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 280f46f9ab..98e02a9294 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -81,7 +81,10 @@ namespace osu.Game.Screens.Select item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } - protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay + { + IsValidMod = isValidMod + }; private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } From 797a8102876808174924486af644de927d7123f4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 13:24:56 +0900 Subject: [PATCH 253/385] Allow unstacking mods --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 30 +++++++++++++++++-- .../Multiplayer/FreeModSelectOverlay.cs | 5 ++++ osu.Game/Utils/ModValidation.cs | 17 +++++++---- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c21b9ba409..db9460c494 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Screens; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -46,9 +47,27 @@ namespace osu.Game.Overlays.Mods protected readonly ModSettingsContainer ModSettingsContainer; + private bool stacked = true; + + /// + /// Whether mod icons should be stacked, or appear as individual buttons. + /// + public bool Stacked + { + get => stacked; + set + { + stacked = value; + updateAvailableMods(); + } + } + [NotNull] private Func isValidMod = m => true; + /// + /// A function that checks whether a given mod is valid. + /// [NotNull] public Func IsValidMod { @@ -419,11 +438,18 @@ namespace osu.Game.Overlays.Mods private void updateAvailableMods() { - if (availableMods.Value == null) + if (availableMods?.Value == null) return; foreach (var section in ModSectionsContainer.Children) - section.Mods = availableMods.Value[section.ModType].Where(IsValidMod); + { + IEnumerable modEnumeration = availableMods.Value[section.ModType]; + + if (!stacked) + modEnumeration = ModValidation.FlattenMods(modEnumeration); + + section.Mods = modEnumeration.Where(IsValidMod); + } } private void selectedModsChanged(ValueChangedEvent> mods) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs index 56e74a8460..8334df1e44 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs @@ -14,6 +14,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { public class FreeModSelectOverlay : ModSelectOverlay { + public FreeModSelectOverlay() + { + Stacked = false; + } + protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); private class FreeModSection : ModSection diff --git a/osu.Game/Utils/ModValidation.cs b/osu.Game/Utils/ModValidation.cs index 0c4d58ab2e..3597396ec4 100644 --- a/osu.Game/Utils/ModValidation.cs +++ b/osu.Game/Utils/ModValidation.cs @@ -42,7 +42,7 @@ namespace osu.Game.Utils var incompatibleTypes = new HashSet(); var incomingTypes = new HashSet(); - foreach (var mod in combination.SelectMany(flattenMod)) + foreach (var mod in combination.SelectMany(FlattenMod)) { // Add the new mod incompatibilities, checking whether any match the existing mod types. foreach (var t in mod.IncompatibleMods) @@ -79,7 +79,7 @@ namespace osu.Game.Utils { var allowedSet = new HashSet(allowedTypes); - return combination.SelectMany(flattenMod) + return combination.SelectMany(FlattenMod) .All(m => allowedSet.Contains(m.GetType())); } @@ -93,20 +93,27 @@ namespace osu.Game.Utils /// The set of incompatible types. /// Whether the given is incompatible. private static bool isModIncompatible(Mod mod, ICollection incompatibleTypes) - => flattenMod(mod) + => FlattenMod(mod) .SelectMany(m => m.GetType().EnumerateBaseTypes()) .Any(incompatibleTypes.Contains); + /// + /// Flattens a set of s, returning a new set with all s removed. + /// + /// The set of s to flatten. + /// The new set, containing all s in recursively with all s removed. + public static IEnumerable FlattenMods(IEnumerable mods) => mods.SelectMany(FlattenMod); + /// /// Flattens a , returning a set of s in-place of any s. /// /// The to flatten. /// A set of singular "flattened" s - private static IEnumerable flattenMod(Mod mod) + public static IEnumerable FlattenMod(Mod mod) { if (mod is MultiMod multi) { - foreach (var m in multi.Mods.SelectMany(flattenMod)) + foreach (var m in multi.Mods.SelectMany(FlattenMod)) yield return m; } else From e02e3cf19a9ad9ace7d21e949ec30e6c9269771e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 13:35:48 +0900 Subject: [PATCH 254/385] Disallow selecting DT/HT/WU/WD as allowable freemods --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index d36ebeec0f..98e3ca3358 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer freeModSelectOverlay = new FreeModSelectOverlay { SelectedMods = { BindTarget = freeMods }, - IsValidMod = isValidMod, + IsValidMod = isValidFreeMod, }; } @@ -147,6 +147,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; + + private bool isValidFreeMod(Mod mod) => isValidMod(mod) && !(mod is ModRateAdjust) && !(mod is ModTimeRamp); } public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> From 4ae10b1e1c88f4d6b6a3fe5ec136abf1e3ead32b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 13:40:59 +0900 Subject: [PATCH 255/385] Add initial UI for selecting extra mods --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 29 ++++++-- .../Multiplayer/MultiplayerMatchSubScreen.cs | 69 +++++++++++++++++-- 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 2449563c73..231fb58836 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -29,6 +30,11 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved(typeof(Room), nameof(Room.Playlist))] protected BindableList Playlist { get; private set; } + /// + /// Any mods applied by/to the local user. + /// + protected readonly Bindable> ExtraMods = new Bindable>(Array.Empty()); + [Resolved] private MusicController music { get; set; } @@ -55,6 +61,8 @@ namespace osu.Game.Screens.OnlinePlay.Match managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); + + ExtraMods.BindValueChanged(_ => updateMods()); } public override void OnEntering(IScreen last) @@ -95,12 +103,17 @@ namespace osu.Game.Screens.OnlinePlay.Match { updateWorkingBeatmap(); - var item = SelectedItem.Value; + if (SelectedItem.Value == null) + return; - Mods.Value = item?.RequiredMods?.ToArray() ?? Array.Empty(); + // Remove any extra mods that are no longer allowed. + ExtraMods.Value = ExtraMods.Value + .Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType())) + .ToList(); - if (item?.Ruleset != null) - Ruleset.Value = item.Ruleset.Value; + updateMods(); + + Ruleset.Value = SelectedItem.Value.Ruleset.Value; } private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); @@ -115,6 +128,14 @@ namespace osu.Game.Screens.OnlinePlay.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } + private void updateMods() + { + if (SelectedItem.Value == null) + return; + + Mods.Value = ExtraMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); + } + private void beginHandlingTrack() { Beatmap.BindValueChanged(applyLoopingToTrack, true); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7c4b6d18ec..95dda1a051 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -14,12 +14,15 @@ using osu.Framework.Screens; using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Overlays.Mods; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; +using osu.Game.Screens.Play.HUD; using osu.Game.Users; +using osuTK; using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList; namespace osu.Game.Screens.OnlinePlay.Multiplayer @@ -37,7 +40,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } + private ModSelectOverlay extraModSelectOverlay; private MultiplayerMatchSettingsOverlay settingsOverlay; + private Drawable extraModsSection; private IBindable isConnected; @@ -129,10 +134,39 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 5 }, - Children = new Drawable[] + Spacing = new Vector2(0, 10), + Children = new[] { - new OverlinedHeader("Beatmap"), - new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OverlinedHeader("Beatmap"), + new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + } + }, + extraModsSection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OverlinedHeader("Extra mods"), + new ModDisplay + { + DisplayUnrankedText = false, + Current = ExtraMods + }, + new PurpleTriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Select", + Action = () => extraModSelectOverlay.Show() + } + } + } } } } @@ -174,6 +208,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Dimension(GridSizeMode.AutoSize), } }, + extraModSelectOverlay = new SoloModSelectOverlay + { + SelectedMods = { BindTarget = ExtraMods }, + Stacked = false, + IsValidMod = _ => false + }, settingsOverlay = new MultiplayerMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, @@ -219,10 +259,31 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return true; } + if (extraModSelectOverlay.State.Value == Visibility.Visible) + { + extraModSelectOverlay.Hide(); + return true; + } + return base.OnBackButton(); } - private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault(); + private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) + { + SelectedItem.Value = Playlist.FirstOrDefault(); + + if (SelectedItem.Value?.AllowedMods.Any() != true) + { + extraModsSection.Hide(); + extraModSelectOverlay.Hide(); + extraModSelectOverlay.IsValidMod = _ => false; + } + else + { + extraModsSection.Show(); + extraModSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType()); + } + } private void onReadyClick() { From b846146f163a0dfe87d8d5e02f00730387b8de04 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 13:58:44 +0900 Subject: [PATCH 256/385] Update mods when resuming room subscreen --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 231fb58836..3c4c6ce040 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -81,6 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Match { base.OnResuming(last); beginHandlingTrack(); + updateMods(); } public override bool OnExiting(IScreen next) From 426569c2a93244a7a31688a0d96529d752858ebc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 14:57:39 +0900 Subject: [PATCH 257/385] Move common song select implementation for online play --- .../Multiplayer/MultiplayerMatchSongSelect.cs | 91 +------------ .../OnlinePlay/OnlinePlaySongSelect.cs | 124 ++++++++++++++++++ osu.Game/Screens/Select/MatchSongSelect.cs | 34 +---- 3 files changed, 130 insertions(+), 119 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 98e3ca3358..e892570066 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -1,25 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; -using System.Linq; -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; @@ -27,67 +20,21 @@ using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { - public class MultiplayerMatchSongSelect : SongSelect, IOnlinePlaySubScreen + public class MultiplayerMatchSongSelect : OnlinePlaySongSelect { - public string ShortTitle => "song selection"; - - public override string Title => ShortTitle.Humanize(); - - [Resolved(typeof(Room), nameof(Room.Playlist))] - private BindableList playlist { get; set; } - [Resolved] private StatefulMultiplayerClient client { get; set; } - private readonly Bindable> freeMods = new Bindable>(Array.Empty()); - - private readonly FreeModSelectOverlay freeModSelectOverlay; private LoadingLayer loadingLayer; - private WorkingBeatmap initialBeatmap; - private RulesetInfo initialRuleset; - private IReadOnlyList initialMods; - - private bool itemSelected; - - public MultiplayerMatchSongSelect() - { - Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - - freeModSelectOverlay = new FreeModSelectOverlay - { - SelectedMods = { BindTarget = freeMods }, - IsValidMod = isValidFreeMod, - }; - } - [BackgroundDependencyLoader] private void load() { AddInternal(loadingLayer = new LoadingLayer(true)); - initialBeatmap = Beatmap.Value; - initialRuleset = Ruleset.Value; - initialMods = Mods.Value.ToList(); - - freeMods.Value = playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); - - FooterPanels.Add(freeModSelectOverlay); } - protected override bool OnStart() + protected override void OnSetItem(PlaylistItem item) { - itemSelected = true; - var item = new PlaylistItem(); - - item.Beatmap.Value = Beatmap.Value.BeatmapInfo; - item.Ruleset.Value = Ruleset.Value; - - item.RequiredMods.Clear(); - item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); - - item.AllowedMods.Clear(); - item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy())); - // If the client is already in a room, update via the client. // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. if (client.Room != null) @@ -112,43 +59,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } else { - playlist.Clear(); - playlist.Add(item); + Playlist.Clear(); + Playlist.Add(item); this.Exit(); } - - return true; - } - - public override bool OnExiting(IScreen next) - { - if (!itemSelected) - { - Beatmap.Value = initialBeatmap; - Ruleset.Value = initialRuleset; - Mods.Value = initialMods; - } - - return base.OnExiting(next); } protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - - protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay - { - IsValidMod = isValidMod - }; - - protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() - { - var buttons = base.CreateFooterButtons().ToList(); - buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay)); - return buttons; - } - - private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; - - private bool isValidFreeMod(Mod mod) => isValidMod(mod) && !(mod is ModRateAdjust) && !(mod is ModTimeRamp); } public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs new file mode 100644 index 0000000000..7c64e00dc4 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -0,0 +1,124 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Online.Rooms; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.Select; + +namespace osu.Game.Screens.OnlinePlay +{ + public abstract class OnlinePlaySongSelect : SongSelect, IOnlinePlaySubScreen + { + public string ShortTitle => "song selection"; + + public override string Title => ShortTitle.Humanize(); + + public override bool AllowEditing => false; + + [Resolved(typeof(Room), nameof(Room.Playlist))] + protected BindableList Playlist { get; private set; } + + private readonly Bindable> freeMods = new Bindable>(Array.Empty()); + private readonly FreeModSelectOverlay freeModSelectOverlay; + + private WorkingBeatmap initialBeatmap; + private RulesetInfo initialRuleset; + private IReadOnlyList initialMods; + private bool itemSelected; + + protected OnlinePlaySongSelect() + { + Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; + + freeModSelectOverlay = new FreeModSelectOverlay + { + SelectedMods = { BindTarget = freeMods }, + IsValidMod = IsValidFreeMod, + }; + } + + [BackgroundDependencyLoader] + private void load() + { + initialBeatmap = Beatmap.Value; + initialRuleset = Ruleset.Value; + initialMods = Mods.Value.ToList(); + + freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + FooterPanels.Add(freeModSelectOverlay); + } + + protected sealed override bool OnStart() + { + itemSelected = true; + + var item = new PlaylistItem(); + + item.Beatmap.Value = Beatmap.Value.BeatmapInfo; + item.Ruleset.Value = Ruleset.Value; + + item.RequiredMods.Clear(); + item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); + + item.AllowedMods.Clear(); + item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy())); + + OnSetItem(item); + return true; + } + + protected abstract void OnSetItem(PlaylistItem item); + + public override bool OnBackButton() + { + if (freeModSelectOverlay.State.Value == Visibility.Visible) + { + freeModSelectOverlay.Hide(); + return true; + } + + return base.OnBackButton(); + } + + public override bool OnExiting(IScreen next) + { + if (!itemSelected) + { + Beatmap.Value = initialBeatmap; + Ruleset.Value = initialRuleset; + Mods.Value = initialMods; + } + + return base.OnExiting(next); + } + + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay + { + IsValidMod = IsValidMod + }; + + protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() + { + var buttons = base.CreateFooterButtons().ToList(); + buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay)); + return buttons; + } + + protected virtual bool IsValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; + + protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod); + } +} diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 98e02a9294..23fe9620fe 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -1,48 +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 System; using System.Linq; -using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Components; namespace osu.Game.Screens.Select { - public class MatchSongSelect : SongSelect, IOnlinePlaySubScreen + public class MatchSongSelect : OnlinePlaySongSelect { - public Action Selected; - - public string ShortTitle => "song selection"; - public override string Title => ShortTitle.Humanize(); - - public override bool AllowEditing => false; - - [Resolved(typeof(Room), nameof(Room.Playlist))] - protected BindableList Playlist { get; private set; } - [Resolved] private BeatmapManager beatmaps { get; set; } - public MatchSongSelect() - { - Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - } - protected override BeatmapDetailArea CreateBeatmapDetailArea() => new MatchBeatmapDetailArea { CreateNewItem = createNewItem }; - protected override bool OnStart() + protected override void OnSetItem(PlaylistItem item) { switch (Playlist.Count) { @@ -56,8 +35,6 @@ namespace osu.Game.Screens.Select } this.Exit(); - - return true; } private void createNewItem() @@ -80,12 +57,5 @@ namespace osu.Game.Screens.Select item.RequiredMods.Clear(); item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } - - protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay - { - IsValidMod = isValidMod - }; - - private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } } From b43e529964b31ac40d5230aa41e68876985579d7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 15:06:50 +0900 Subject: [PATCH 258/385] Fix allowed mods being copied into required mods --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index e892570066..0c22813e56 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.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 System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -33,6 +35,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AddInternal(loadingLayer = new LoadingLayer(true)); } + protected override void LoadComplete() + { + base.LoadComplete(); + + Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + } + protected override void OnSetItem(PlaylistItem item) { // If the client is already in a room, update via the client. From 0909c73ead3d4c45495c8bb5d55dfac16386038a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 15:07:56 +0900 Subject: [PATCH 259/385] Once again disallow DT/etc as allowable mods --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 0c22813e56..80bb7c7ac2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -75,6 +75,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); + + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust); } public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> From 05982f42ab8a991d9056a7ad38f947c0edc4c4cd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Feb 2021 16:43:53 +0900 Subject: [PATCH 260/385] Add more comprehensive commenting and simplify base call logic We can call the base method regardless for better safety. Worst case it's just going to run `Stop()` twice anyway. --- .../Drawables/DrawableStoryboardSample.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index fcbffda227..7b16009859 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -46,14 +46,15 @@ namespace osu.Game.Storyboards.Drawables { if (!RequestedPlaying) return; - // non-looping storyboard samples should be stopped immediately when sample playback is disabled - if (!Looping) + if (!Looping && disabled.NewValue) { - if (disabled.NewValue) - Stop(); + // the default behaviour for sample disabling is to allow one-shot samples to play out. + // storyboards regularly have long running samples that can cause this behaviour to lead to unintended results. + // for this reason, we immediately stop such samples. + Stop(); } - else - base.SamplePlaybackDisabledChanged(disabled); + + base.SamplePlaybackDisabledChanged(disabled); } protected override void Update() From 49e62c3a4b7118c1f3c81c6af357534bd541d8ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Feb 2021 11:02:08 +0300 Subject: [PATCH 261/385] Apply documentation changes Co-authored-by: Dean Herbert --- osu.Game/Overlays/Chat/DrawableChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index f1aa387c17..4c8513b1d5 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Chat } /// - /// An with functionality to automatically scrolls whenever the maximum scrollable distance increases. + /// An with functionality to automatically scroll whenever the maximum scrollable distance increases. /// private class ChannelScrollContainer : OsuScrollContainer { @@ -246,7 +246,7 @@ namespace osu.Game.Overlays.Chat private float? lastExtent; /// - /// Whether this should automatically scroll to end on the next call to . + /// Whether this container should automatically scroll to end on the next call to . /// public bool ShouldAutoScroll { get; private set; } = true; From fabb0eeb29a35c7e0d212d9c7be9f1aad8e90c35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Feb 2021 17:27:14 +0900 Subject: [PATCH 262/385] Add signalr messagepack key attribute --- osu.Game/Online/Rooms/BeatmapAvailability.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Rooms/BeatmapAvailability.cs b/osu.Game/Online/Rooms/BeatmapAvailability.cs index 4ce797e583..2adeb9b959 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -22,6 +22,7 @@ namespace osu.Game.Online.Rooms /// /// The beatmap's downloading progress, null when not in state. /// + [Key(1)] public readonly float? DownloadProgress; [JsonConstructor] From 1d8de2f718916b907b24bf1b411ebb627258cbd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Feb 2021 17:32:54 +0900 Subject: [PATCH 263/385] Rename class to better match purpose --- ... TestSceneMultiplayerBeatmapAvailabilityTracker.cs} | 10 +++++----- ...er.cs => MultiplayerBeatmapAvailablilityTracker.cs} | 7 +++---- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) rename osu.Game.Tests/Online/{TestSceneMultiplayerBeatmapTracker.cs => TestSceneMultiplayerBeatmapAvailabilityTracker.cs} (94%) rename osu.Game/Online/Rooms/{MultiplayerBeatmapTracker.cs => MultiplayerBeatmapAvailablilityTracker.cs} (93%) diff --git a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs similarity index 94% rename from osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs rename to osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs index 9caecc198a..3c3793670a 100644 --- a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapTracker.cs +++ b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs @@ -28,7 +28,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Online { [HeadlessTest] - public class TestSceneMultiplayerBeatmapTracker : OsuTestScene + public class TestSceneMultiplayerBeatmapAvailabilityTracker : OsuTestScene { private RulesetStore rulesets; private TestBeatmapManager beatmaps; @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Online private BeatmapSetInfo testBeatmapSet; private readonly Bindable selectedItem = new Bindable(); - private MultiplayerBeatmapTracker tracker; + private MultiplayerBeatmapAvailablilityTracker availablilityTracker; [BackgroundDependencyLoader] private void load(AudioManager audio, GameHost host) @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Online Ruleset = { Value = testBeatmapInfo.Ruleset }, }; - Child = tracker = new MultiplayerBeatmapTracker + Child = availablilityTracker = new MultiplayerBeatmapAvailablilityTracker { SelectedItem = { BindTarget = selectedItem, } }; @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Online }); addAvailabilityCheckStep("state still not downloaded", BeatmapAvailability.NotDownloaded); - AddStep("recreate tracker", () => Child = tracker = new MultiplayerBeatmapTracker + AddStep("recreate tracker", () => Child = availablilityTracker = new MultiplayerBeatmapAvailablilityTracker { SelectedItem = { BindTarget = selectedItem } }); @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Online { // In DownloadTrackingComposite, state changes are scheduled one frame later, wait one step. AddWaitStep("wait for potential change", 1); - AddAssert(description, () => tracker.Availability.Value.Equals(expected.Invoke())); + AddAssert(description, () => availablilityTracker.Availability.Value.Equals(expected.Invoke())); } private static BeatmapInfo getTestBeatmapInfo(string archiveFile) diff --git a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs b/osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs similarity index 93% rename from osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs rename to osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs index 28e4872ad3..578c4db2f8 100644 --- a/osu.Game/Online/Rooms/MultiplayerBeatmapTracker.cs +++ b/osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class MultiplayerBeatmapTracker : DownloadTrackingComposite + public class MultiplayerBeatmapAvailablilityTracker : DownloadTrackingComposite { public readonly IBindable SelectedItem = new Bindable(); @@ -26,11 +26,10 @@ namespace osu.Game.Online.Rooms private readonly Bindable availability = new Bindable(); - public MultiplayerBeatmapTracker() + public MultiplayerBeatmapAvailablilityTracker() { State.BindValueChanged(_ => updateAvailability()); - Progress.BindValueChanged(_ => updateAvailability()); - updateAvailability(); + Progress.BindValueChanged(_ => updateAvailability(), true); } protected override void LoadComplete() diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index c049d4be20..f2cb1120cb 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -41,11 +41,11 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected readonly MultiplayerBeatmapTracker BeatmapTracker; + protected readonly MultiplayerBeatmapAvailablilityTracker BeatmapAvailablilityTracker; protected RoomSubScreen() { - InternalChild = BeatmapTracker = new MultiplayerBeatmapTracker + InternalChild = BeatmapAvailablilityTracker = new MultiplayerBeatmapAvailablilityTracker { SelectedItem = { BindTarget = SelectedItem }, }; From ac2a995041590370e31b2b5ef9fb440dcb20d43c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 17:54:56 +0900 Subject: [PATCH 264/385] Add user and panel states --- .../TestSceneMultiplayerParticipantsList.cs | 25 ++++++++++++++ .../Online/Multiplayer/IMultiplayerClient.cs | 4 +++ .../Multiplayer/IMultiplayerRoomServer.cs | 4 +++ .../Online/Multiplayer/MultiplayerRoomUser.cs | 7 ++++ .../Multiplayer/StatefulMultiplayerClient.cs | 26 +++++++++++++++ .../Multiplayer/MultiplayerMatchSubScreen.cs | 11 +++++++ .../Participants/ParticipantPanel.cs | 33 ++++++++++++++++++- osu.Game/Screens/Play/HUD/ModDisplay.cs | 14 +++++--- .../Multiplayer/TestMultiplayerClient.cs | 17 ++++++++++ 9 files changed, 136 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 968a869532..9aa1f2cf99 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -8,6 +8,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Users; using osuTK; @@ -123,5 +125,28 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); } + + [Test] + public void TestUserWithMods() + { + AddStep("add user", () => + { + Client.AddUser(new User + { + Id = 0, + Username = $"User 0", + CurrentModeRank = RNG.Next(1, 100000), + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }); + + Client.ChangeUserExtraMods(0, new Mod[] + { + new OsuModHardRock(), + new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } } + }); + }); + + AddToggleStep("toggle ready state", v => Client.ChangeUserState(0, v ? MultiplayerUserState.Ready : MultiplayerUserState.Idle)); + } } } diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 19dd473230..37f60ab036 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.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 System.Collections.Generic; using System.Threading.Tasks; +using osu.Game.Online.API; using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer @@ -55,6 +57,8 @@ namespace osu.Game.Online.Multiplayer /// The new beatmap availability state of the user. Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability); + Task UserExtraModsChanged(int userId, IEnumerable mods); + /// /// 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 09816974a7..484acfe957 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.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 System.Collections.Generic; using System.Threading.Tasks; +using osu.Game.Online.API; using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer @@ -47,6 +49,8 @@ namespace osu.Game.Online.Multiplayer /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + Task ChangeExtraMods(IEnumerable newMods); + /// /// As the host of a room, start the match. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 2590acbc81..d7f7f9135e 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -4,7 +4,11 @@ #nullable enable using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; +using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Users; @@ -22,6 +26,9 @@ namespace osu.Game.Online.Multiplayer /// public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); + [NotNull] + public IEnumerable ExtraMods { get; set; } = Enumerable.Empty(); + public User? User { get; set; } [JsonConstructor] diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index e5b07ddfb4..33dcf1e8b4 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -22,6 +22,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Users; using osu.Game.Utils; @@ -231,6 +232,10 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + public Task ChangeExtraMods(IEnumerable newMods) => ChangeExtraMods(newMods.Select(m => new APIMod(m)).ToList()); + + public abstract Task ChangeExtraMods(IEnumerable newMods); + public abstract Task StartMatch(); Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state) @@ -379,6 +384,27 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } + public Task UserExtraModsChanged(int userId, IEnumerable mods) + { + if (Room == null) + return Task.CompletedTask; + + Scheduler.Add(() => + { + var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); + + // errors here are not critical - user mods is mostly for display. + if (user == null) + return; + + user.ExtraMods = mods; + + RoomUpdated?.Invoke(); + }, false); + + return Task.CompletedTask; + } + Task IMultiplayerClient.LoadRequested() { if (Room == null) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 95dda1a051..c1025e73f8 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; @@ -15,6 +16,7 @@ using osu.Game.Extensions; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; @@ -240,6 +242,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); Playlist.BindCollectionChanged(onPlaylistChanged, true); + ExtraMods.BindValueChanged(onExtraModsChanged); client.LoadRequested += onLoadRequested; @@ -285,6 +288,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } + private void onExtraModsChanged(ValueChangedEvent> extraMods) + { + if (client.Room == null) + return; + + client.ChangeExtraMods(extraMods.NewValue).CatchUnobservedExceptions(); + } + private void onReadyClick() { Debug.Assert(readyClickOperation == null); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index f99655e305..059e9e518d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -16,6 +17,8 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; +using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osu.Game.Users.Drawables; using osuTK; @@ -30,6 +33,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + + private ModDisplay extraModsDisplay; private StateDisplay userStateDisplay; private SpriteIcon crown; @@ -122,11 +129,32 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } } }, - userStateDisplay = new StateDisplay + new FillFlowContainer { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Margin = new MarginPadding { Right = 10 }, + Spacing = new Vector2(10), + Children = new Drawable[] + { + extraModsDisplay = new ModDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.5f), + ExpansionMode = ExpansionMode.AlwaysContracted, + DisplayUnrankedText = false, + ExpandOnAppear = false + }, + userStateDisplay = new StateDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AlwaysPresent = true + } + } } } } @@ -143,7 +171,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants const double fade_time = 50; + var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance(); + userStateDisplay.Status = User.State; + extraModsDisplay.Current.Value = User.ExtraMods.Select(m => m.ToMod(ruleset)).ToList(); if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 68d019bf71..2d5b07f056 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -26,6 +26,8 @@ namespace osu.Game.Screens.Play.HUD public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; + public bool ExpandOnAppear = true; + private readonly Bindable> current = new Bindable>(); public Bindable> Current @@ -108,10 +110,14 @@ namespace osu.Game.Screens.Play.HUD else unrankedText.Hide(); - expand(); - - using (iconsContainer.BeginDelayedSequence(1200)) - contract(); + if (ExpandOnAppear) + { + expand(); + using (iconsContainer.BeginDelayedSequence(1200)) + contract(); + } + else + iconsContainer.TransformSpacingTo(new Vector2(-25, 0)); } private void expand() diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 7fbc770351..e699e7fb34 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -3,6 +3,7 @@ #nullable enable +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -11,6 +12,7 @@ using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Mods; using osu.Game.Users; namespace osu.Game.Tests.Visual.Multiplayer @@ -122,6 +124,21 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } + public void ChangeUserExtraMods(int userId, IEnumerable newMods) + => ChangeUserExtraMods(userId, newMods.Select(m => new APIMod(m)).ToList()); + + public void ChangeUserExtraMods(int userId, IEnumerable newMods) + { + Debug.Assert(Room != null); + ((IMultiplayerClient)this).UserExtraModsChanged(userId, newMods.ToList()); + } + + public override Task ChangeExtraMods(IEnumerable newMods) + { + ChangeUserExtraMods(api.LocalUser.Value.Id, newMods); + return Task.CompletedTask; + } + public override Task StartMatch() { Debug.Assert(Room != null); From f53896360715341641dfc7971cb04f66d19fd91d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 17:57:32 +0900 Subject: [PATCH 265/385] Extra mods -> user mods --- .../TestSceneMultiplayerParticipantsList.cs | 2 +- .../Online/Multiplayer/IMultiplayerClient.cs | 2 +- .../Multiplayer/IMultiplayerRoomServer.cs | 2 +- .../Online/Multiplayer/MultiplayerClient.cs | 10 ++++++ .../Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- .../Multiplayer/StatefulMultiplayerClient.cs | 8 ++--- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 12 +++---- .../Multiplayer/MultiplayerMatchSubScreen.cs | 34 +++++++++---------- .../Participants/ParticipantPanel.cs | 6 ++-- .../Multiplayer/TestMultiplayerClient.cs | 12 +++---- 10 files changed, 50 insertions(+), 40 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 9aa1f2cf99..8caba5d9c8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }); - Client.ChangeUserExtraMods(0, new Mod[] + Client.ChangeUserMods(0, new Mod[] { new OsuModHardRock(), new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } } diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 37f60ab036..f22b0e4e28 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -57,7 +57,7 @@ namespace osu.Game.Online.Multiplayer /// The new beatmap availability state of the user. Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability); - Task UserExtraModsChanged(int userId, IEnumerable mods); + Task UserModsChanged(int userId, IEnumerable mods); /// /// 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 484acfe957..71555ae23d 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.Multiplayer /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); - Task ChangeExtraMods(IEnumerable newMods); + Task ChangeUserMods(IEnumerable newMods); /// /// 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 50dc8f661c..ecf314c1e5 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -4,6 +4,7 @@ #nullable enable using System; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -84,6 +85,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); + connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.Closed += async ex => { @@ -182,6 +184,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability); } + public override Task ChangeUserMods(IEnumerable newMods) + { + if (!isConnected.Value) + return Task.CompletedTask; + + return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods); + } + public override Task StartMatch() { if (!isConnected.Value) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index d7f7f9135e..4c9643bfce 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -27,7 +27,7 @@ namespace osu.Game.Online.Multiplayer public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); [NotNull] - public IEnumerable ExtraMods { get; set; } = Enumerable.Empty(); + public IEnumerable UserMods { get; set; } = Enumerable.Empty(); public User? User { get; set; } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 33dcf1e8b4..a0e903e89a 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -232,9 +232,9 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); - public Task ChangeExtraMods(IEnumerable newMods) => ChangeExtraMods(newMods.Select(m => new APIMod(m)).ToList()); + public Task ChangeUserMods(IEnumerable newMods) => ChangeUserMods(newMods.Select(m => new APIMod(m)).ToList()); - public abstract Task ChangeExtraMods(IEnumerable newMods); + public abstract Task ChangeUserMods(IEnumerable newMods); public abstract Task StartMatch(); @@ -384,7 +384,7 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - public Task UserExtraModsChanged(int userId, IEnumerable mods) + public Task UserModsChanged(int userId, IEnumerable mods) { if (Room == null) return Task.CompletedTask; @@ -397,7 +397,7 @@ namespace osu.Game.Online.Multiplayer if (user == null) return; - user.ExtraMods = mods; + user.UserMods = mods; RoomUpdated?.Invoke(); }, false); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 3c4c6ce040..6367aa54a7 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Match /// /// Any mods applied by/to the local user. /// - protected readonly Bindable> ExtraMods = new Bindable>(Array.Empty()); + protected readonly Bindable> UserMods = new Bindable>(Array.Empty()); [Resolved] private MusicController music { get; set; } @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Match managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); - ExtraMods.BindValueChanged(_ => updateMods()); + UserMods.BindValueChanged(_ => updateMods()); } public override void OnEntering(IScreen last) @@ -108,9 +108,9 @@ namespace osu.Game.Screens.OnlinePlay.Match return; // Remove any extra mods that are no longer allowed. - ExtraMods.Value = ExtraMods.Value - .Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType())) - .ToList(); + UserMods.Value = UserMods.Value + .Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType())) + .ToList(); updateMods(); @@ -134,7 +134,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (SelectedItem.Value == null) return; - Mods.Value = ExtraMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); + Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); } private void beginHandlingTrack() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index c1025e73f8..a31a3e51ee 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -42,9 +42,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved] private OngoingOperationTracker ongoingOperationTracker { get; set; } - private ModSelectOverlay extraModSelectOverlay; + private ModSelectOverlay userModsSelectOverlay; private MultiplayerMatchSettingsOverlay settingsOverlay; - private Drawable extraModsSection; + private Drawable userModsSection; private IBindable isConnected; @@ -149,7 +149,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } } }, - extraModsSection = new FillFlowContainer + userModsSection = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -159,13 +159,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new ModDisplay { DisplayUnrankedText = false, - Current = ExtraMods + Current = UserMods }, new PurpleTriangleButton { RelativeSizeAxes = Axes.X, Text = "Select", - Action = () => extraModSelectOverlay.Show() + Action = () => userModsSelectOverlay.Show() } } } @@ -210,9 +210,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Dimension(GridSizeMode.AutoSize), } }, - extraModSelectOverlay = new SoloModSelectOverlay + userModsSelectOverlay = new SoloModSelectOverlay { - SelectedMods = { BindTarget = ExtraMods }, + SelectedMods = { BindTarget = UserMods }, Stacked = false, IsValidMod = _ => false }, @@ -242,7 +242,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); Playlist.BindCollectionChanged(onPlaylistChanged, true); - ExtraMods.BindValueChanged(onExtraModsChanged); + UserMods.BindValueChanged(onUserModsChanged); client.LoadRequested += onLoadRequested; @@ -262,9 +262,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return true; } - if (extraModSelectOverlay.State.Value == Visibility.Visible) + if (userModsSelectOverlay.State.Value == Visibility.Visible) { - extraModSelectOverlay.Hide(); + userModsSelectOverlay.Hide(); return true; } @@ -277,23 +277,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (SelectedItem.Value?.AllowedMods.Any() != true) { - extraModsSection.Hide(); - extraModSelectOverlay.Hide(); - extraModSelectOverlay.IsValidMod = _ => false; + userModsSection.Hide(); + userModsSelectOverlay.Hide(); + userModsSelectOverlay.IsValidMod = _ => false; } else { - extraModsSection.Show(); - extraModSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType()); + userModsSection.Show(); + userModsSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType()); } } - private void onExtraModsChanged(ValueChangedEvent> extraMods) + private void onUserModsChanged(ValueChangedEvent> mods) { if (client.Room == null) return; - client.ChangeExtraMods(extraMods.NewValue).CatchUnobservedExceptions(); + client.ChangeUserMods(mods.NewValue).CatchUnobservedExceptions(); } private void onReadyClick() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 059e9e518d..a782da4c39 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants [Resolved] private RulesetStore rulesets { get; set; } - private ModDisplay extraModsDisplay; + private ModDisplay userModsDisplay; private StateDisplay userStateDisplay; private SpriteIcon crown; @@ -139,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Spacing = new Vector2(10), Children = new Drawable[] { - extraModsDisplay = new ModDisplay + userModsDisplay = new ModDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -174,7 +174,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance(); userStateDisplay.Status = User.State; - extraModsDisplay.Current.Value = User.ExtraMods.Select(m => m.ToMod(ruleset)).ToList(); + userModsDisplay.Current.Value = User.UserMods.Select(m => m.ToMod(ruleset)).ToList(); if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index e699e7fb34..d0d41e56c3 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -124,18 +124,18 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public void ChangeUserExtraMods(int userId, IEnumerable newMods) - => ChangeUserExtraMods(userId, newMods.Select(m => new APIMod(m)).ToList()); + public void ChangeUserMods(int userId, IEnumerable newMods) + => ChangeUserMods(userId, newMods.Select(m => new APIMod(m)).ToList()); - public void ChangeUserExtraMods(int userId, IEnumerable newMods) + public void ChangeUserMods(int userId, IEnumerable newMods) { Debug.Assert(Room != null); - ((IMultiplayerClient)this).UserExtraModsChanged(userId, newMods.ToList()); + ((IMultiplayerClient)this).UserModsChanged(userId, newMods.ToList()); } - public override Task ChangeExtraMods(IEnumerable newMods) + public override Task ChangeUserMods(IEnumerable newMods) { - ChangeUserExtraMods(api.LocalUser.Value.Id, newMods); + ChangeUserMods(api.LocalUser.Value.Id, newMods); return Task.CompletedTask; } From 3cd30d284eb01689fab740342244ad33e37d66f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:08:49 +0900 Subject: [PATCH 266/385] Renamespace --- .../TestSceneFreeModSelectOverlay.cs | 2 +- .../OnlinePlay/Match/FooterButtonFreeMods.cs | 63 +++++++++++++++++++ .../FreeModSelectOverlay.cs | 2 +- .../Multiplayer/MultiplayerMatchSongSelect.cs | 54 ---------------- .../OnlinePlay/OnlinePlaySongSelect.cs | 2 +- 5 files changed, 66 insertions(+), 57 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs rename osu.Game/Screens/OnlinePlay/{Multiplayer => Match}/FreeModSelectOverlay.cs (98%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 960402df88..b1700d5b6e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.Mods; -using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Match; namespace osu.Game.Tests.Visual.Multiplayer { diff --git a/osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs new file mode 100644 index 0000000000..ca2db877c3 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Select; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Match +{ + public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> + { + public Bindable> Current + { + get => modDisplay.Current; + set => modDisplay.Current = value; + } + + private readonly ModDisplay modDisplay; + + public FooterButtonFreeMods() + { + ButtonContentContainer.Add(modDisplay = new ModDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + DisplayUnrankedText = false, + Scale = new Vector2(0.8f), + ExpansionMode = ExpansionMode.AlwaysContracted, + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + SelectedColour = colours.Yellow; + DeselectedColour = SelectedColour.Opacity(0.5f); + Text = @"freemods"; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => updateModDisplay(), true); + } + + private void updateModDisplay() + { + if (Current.Value?.Count > 0) + modDisplay.FadeIn(); + else + modDisplay.FadeOut(); + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs similarity index 98% rename from osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs rename to osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index 8334df1e44..aba86a3d72 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; -namespace osu.Game.Screens.OnlinePlay.Multiplayer +namespace osu.Game.Screens.OnlinePlay.Match { public class FreeModSelectOverlay : ModSelectOverlay { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 80bb7c7ac2..e6b7656986 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -2,23 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; -using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -78,50 +70,4 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust); } - - public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> - { - public Bindable> Current - { - get => modDisplay.Current; - set => modDisplay.Current = value; - } - - private readonly ModDisplay modDisplay; - - public FooterButtonFreeMods() - { - ButtonContentContainer.Add(modDisplay = new ModDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - DisplayUnrankedText = false, - Scale = new Vector2(0.8f), - ExpansionMode = ExpansionMode.AlwaysContracted, - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - SelectedColour = colours.Yellow; - DeselectedColour = SelectedColour.Opacity(0.5f); - Text = @"freemods"; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Current.BindValueChanged(_ => updateModDisplay(), true); - } - - private void updateModDisplay() - { - if (Current.Value?.Count > 0) - modDisplay.FadeIn(); - else - modDisplay.FadeOut(); - } - } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 7c64e00dc4..b75bf93204 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -15,7 +15,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.OnlinePlay.Multiplayer; +using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.Select; namespace osu.Game.Screens.OnlinePlay From 3e74f8fd9e260c48b4240a98bd7bfd2eef2c0598 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:11:20 +0900 Subject: [PATCH 267/385] Disable customisation of freemods, move stacking to property --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 28 ++++++++----------- .../OnlinePlay/Match/FreeModSelectOverlay.cs | 7 ++--- .../Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++-- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index db9460c494..56246a031d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -39,6 +39,16 @@ namespace osu.Game.Overlays.Mods protected readonly OsuSpriteText MultiplierLabel; + /// + /// Whether to allow customisation of mod settings. + /// + protected virtual bool AllowCustomisation => true; + + /// + /// Whether mod icons should be stacked, or appear as individual buttons. + /// + protected virtual bool Stacked => true; + protected override bool BlockNonPositionalInput => false; protected override bool DimMainContent => false; @@ -47,21 +57,6 @@ namespace osu.Game.Overlays.Mods protected readonly ModSettingsContainer ModSettingsContainer; - private bool stacked = true; - - /// - /// Whether mod icons should be stacked, or appear as individual buttons. - /// - public bool Stacked - { - get => stacked; - set - { - stacked = value; - updateAvailableMods(); - } - } - [NotNull] private Func isValidMod = m => true; @@ -307,6 +302,7 @@ namespace osu.Game.Overlays.Mods CustomiseButton = new TriangleButton { Width = 180, + Alpha = AllowCustomisation ? 1 : 0, Text = "Customisation", Action = () => ModSettingsContainer.ToggleVisibility(), Enabled = { Value = false }, @@ -445,7 +441,7 @@ namespace osu.Game.Overlays.Mods { IEnumerable modEnumeration = availableMods.Value[section.ModType]; - if (!stacked) + if (!Stacked) modEnumeration = ModValidation.FlattenMods(modEnumeration); section.Mods = modEnumeration.Where(IsValidMod); diff --git a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index aba86a3d72..0d62cc3d37 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -14,10 +14,9 @@ namespace osu.Game.Screens.OnlinePlay.Match { public class FreeModSelectOverlay : ModSelectOverlay { - public FreeModSelectOverlay() - { - Stacked = false; - } + protected override bool AllowCustomisation => false; + + protected override bool Stacked => false; protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index a31a3e51ee..22a58add70 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -210,10 +210,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Dimension(GridSizeMode.AutoSize), } }, - userModsSelectOverlay = new SoloModSelectOverlay + userModsSelectOverlay = new UserModSelectOverlay { SelectedMods = { BindTarget = UserMods }, - Stacked = false, IsValidMod = _ => false }, settingsOverlay = new MultiplayerMatchSettingsOverlay @@ -352,5 +351,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client != null) client.LoadRequested -= onLoadRequested; } + + private class UserModSelectOverlay : SoloModSelectOverlay + { + protected override bool Stacked => false; + } } } From e134af82f52373cd3b360ab31139271c5d91007a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:16:38 +0900 Subject: [PATCH 268/385] Stack freemods for the local user --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 +++++++++++- .../Multiplayer/MultiplayerMatchSubScreen.cs | 7 +------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 56246a031d..2e69f15ff5 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -444,10 +444,20 @@ namespace osu.Game.Overlays.Mods if (!Stacked) modEnumeration = ModValidation.FlattenMods(modEnumeration); - section.Mods = modEnumeration.Where(IsValidMod); + section.Mods = modEnumeration.Select(validModOrNull).Where(m => m != null); } } + [CanBeNull] + private Mod validModOrNull([NotNull] Mod mod) + { + if (!(mod is MultiMod multi)) + return IsValidMod(mod) ? mod : null; + + var validSubset = multi.Mods.Select(validModOrNull).Where(m => m != null).ToArray(); + return validSubset.Length == 0 ? null : new MultiMod(validSubset); + } + private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 22a58add70..659551abfd 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -210,7 +210,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Dimension(GridSizeMode.AutoSize), } }, - userModsSelectOverlay = new UserModSelectOverlay + userModsSelectOverlay = new SoloModSelectOverlay { SelectedMods = { BindTarget = UserMods }, IsValidMod = _ => false @@ -351,10 +351,5 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client != null) client.LoadRequested -= onLoadRequested; } - - private class UserModSelectOverlay : SoloModSelectOverlay - { - protected override bool Stacked => false; - } } } From 51cb2887172fb330a8b5ff40961e458c08ef7024 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:18:59 +0900 Subject: [PATCH 269/385] Reduce mod selection height --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 659551abfd..87ae723c63 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -210,10 +210,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer new Dimension(GridSizeMode.AutoSize), } }, - userModsSelectOverlay = new SoloModSelectOverlay + new Container { - SelectedMods = { BindTarget = UserMods }, - IsValidMod = _ => false + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Child = userModsSelectOverlay = new SoloModSelectOverlay + { + SelectedMods = { BindTarget = UserMods }, + IsValidMod = _ => false + } }, settingsOverlay = new MultiplayerMatchSettingsOverlay { From 3a906a89fce84a1dd7195af89aa7808276c1c70a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:25:09 +0900 Subject: [PATCH 270/385] Pin mod position in participant panels --- .../TestSceneMultiplayerParticipantsList.cs | 6 +++- .../Participants/ParticipantPanel.cs | 33 ++++++++----------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 8caba5d9c8..768dc6512c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -146,7 +146,11 @@ namespace osu.Game.Tests.Visual.Multiplayer }); }); - AddToggleStep("toggle ready state", v => Client.ChangeUserState(0, v ? MultiplayerUserState.Ready : MultiplayerUserState.Idle)); + for (var i = MultiplayerUserState.Idle; i < MultiplayerUserState.Results; i++) + { + var state = i; + AddStep($"set state: {state}", () => Client.ChangeUserState(0, state)); + } } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index a782da4c39..f6a60c8f57 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -129,32 +129,25 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants } } }, - new FillFlowContainer + new Container { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Margin = new MarginPadding { Right = 10 }, - Spacing = new Vector2(10), - Children = new Drawable[] + Margin = new MarginPadding { Right = 70 }, + Child = userModsDisplay = new ModDisplay { - userModsDisplay = new ModDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.5f), - ExpansionMode = ExpansionMode.AlwaysContracted, - DisplayUnrankedText = false, - ExpandOnAppear = false - }, - userStateDisplay = new StateDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AlwaysPresent = true - } + Scale = new Vector2(0.5f), + ExpansionMode = ExpansionMode.AlwaysContracted, + DisplayUnrankedText = false, + ExpandOnAppear = false } + }, + userStateDisplay = new StateDisplay + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding { Right = 10 }, } } } From 89a42d60fbc8f33c6cd28d2baa617b33c57b1312 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:50:32 +0900 Subject: [PATCH 271/385] General cleanup --- osu.Game.Tests/Mods/ModValidationTest.cs | 14 ++++----- .../TestSceneFreeModSelectOverlay.cs | 5 +-- .../Online/Multiplayer/IMultiplayerClient.cs | 5 +++ .../Multiplayer/IMultiplayerRoomServer.cs | 4 +++ .../Online/Multiplayer/MultiplayerRoomUser.cs | 3 ++ .../Multiplayer/StatefulMultiplayerClient.cs | 6 +++- osu.Game/Overlays/Mods/ModButton.cs | 8 ++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 +++++-- .../OnlinePlay/Match/FreeModSelectOverlay.cs | 31 ++----------------- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- .../OnlinePlay/OnlinePlaySongSelect.cs | 18 +++++++++-- osu.Game/Screens/Play/HUD/ModDisplay.cs | 3 ++ osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +++ .../Utils/{ModValidation.cs => ModUtils.cs} | 24 +++----------- 16 files changed, 71 insertions(+), 72 deletions(-) rename osu.Game/Utils/{ModValidation.cs => ModUtils.cs} (79%) diff --git a/osu.Game.Tests/Mods/ModValidationTest.cs b/osu.Game.Tests/Mods/ModValidationTest.cs index c7a7e242e9..991adc221e 100644 --- a/osu.Game.Tests/Mods/ModValidationTest.cs +++ b/osu.Game.Tests/Mods/ModValidationTest.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.Mods public void TestModIsCompatibleByItself() { var mod = new Mock(); - Assert.That(ModValidation.CheckCompatible(new[] { mod.Object })); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object })); } [Test] @@ -27,8 +27,8 @@ namespace osu.Game.Tests.Mods mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() }); // Test both orderings. - Assert.That(ModValidation.CheckCompatible(new[] { mod1.Object, mod2.Object }), Is.False); - Assert.That(ModValidation.CheckCompatible(new[] { mod2.Object, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); } [Test] @@ -43,22 +43,22 @@ namespace osu.Game.Tests.Mods var multiMod = new MultiMod(new MultiMod(mod2.Object)); // Test both orderings. - Assert.That(ModValidation.CheckCompatible(new[] { multiMod, mod1.Object }), Is.False); - Assert.That(ModValidation.CheckCompatible(new[] { mod1.Object, multiMod }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { multiMod, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); } [Test] public void TestAllowedThroughMostDerivedType() { var mod = new Mock(); - Assert.That(ModValidation.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); + Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); } [Test] public void TestNotAllowedThroughBaseType() { var mod = new Mock(); - Assert.That(ModValidation.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); + Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index b1700d5b6e..e3342eb6a0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -3,19 +3,16 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; -using osu.Game.Overlays.Mods; using osu.Game.Screens.OnlinePlay.Match; namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneFreeModSelectOverlay : MultiplayerTestScene { - private ModSelectOverlay overlay; - [SetUp] public new void Setup() => Schedule(() => { - Child = overlay = new FreeModSelectOverlay + Child = new FreeModSelectOverlay { State = { Value = Visibility.Visible } }; diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index f22b0e4e28..6d7b9d24d6 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -57,6 +57,11 @@ namespace osu.Game.Online.Multiplayer /// The new beatmap availability state of the user. Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability); + /// + /// Signals that a user in this room changed their local mods. + /// + /// The ID of the user whose mods have changed. + /// The user's new local mods. Task UserModsChanged(int userId, IEnumerable mods); /// diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 71555ae23d..3527ce6314 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -49,6 +49,10 @@ namespace osu.Game.Online.Multiplayer /// The proposed new beatmap availability state. Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + /// + /// Change the local user's mods in the currently joined room. + /// + /// The proposed new mods, excluding any required by the room itself. Task ChangeUserMods(IEnumerable newMods); /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 4c9643bfce..7de24826dd 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -26,6 +26,9 @@ namespace osu.Game.Online.Multiplayer /// public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable(); + /// + /// Any mods applicable only to the local user. + /// [NotNull] public IEnumerable UserMods { get; set; } = Enumerable.Empty(); diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index a0e903e89a..597bee2764 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -232,6 +232,10 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + /// + /// Change the local user's mods in the currently joined room. + /// + /// The proposed new mods, excluding any required by the room itself. public Task ChangeUserMods(IEnumerable newMods) => ChangeUserMods(newMods.Select(m => new APIMod(m)).ToList()); public abstract Task ChangeUserMods(IEnumerable newMods); @@ -393,7 +397,7 @@ namespace osu.Game.Online.Multiplayer { var user = Room?.Users.SingleOrDefault(u => u.UserID == userId); - // errors here are not critical - user mods is mostly for display. + // errors here are not critical - user mods are mostly for display. if (user == null) return; diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 9ea4f65eb2..ab8efdabcc 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -174,7 +174,7 @@ namespace osu.Game.Overlays.Mods switch (e.Button) { case MouseButton.Right: - OnRightClick(e); + SelectNext(-1); break; } } @@ -183,12 +183,8 @@ namespace osu.Game.Overlays.Mods protected override bool OnClick(ClickEvent e) { SelectNext(1); - return true; - } - protected virtual void OnRightClick(MouseUpEvent e) - { - SelectNext(-1); + return true; } /// diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 2e69f15ff5..2950dc7489 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Mods private Func isValidMod = m => true; /// - /// A function that checks whether a given mod is valid. + /// A function that checks whether a given mod is selectable. /// [NotNull] public Func IsValidMod @@ -442,12 +442,20 @@ namespace osu.Game.Overlays.Mods IEnumerable modEnumeration = availableMods.Value[section.ModType]; if (!Stacked) - modEnumeration = ModValidation.FlattenMods(modEnumeration); + modEnumeration = ModUtils.FlattenMods(modEnumeration); section.Mods = modEnumeration.Select(validModOrNull).Where(m => m != null); } } + /// + /// Returns a valid form of a given if possible, or null otherwise. + /// + /// + /// This is a recursive process during which any invalid mods are culled while preserving structures where possible. + /// + /// The to check. + /// A valid form of if exists, or null otherwise. [CanBeNull] private Mod validModOrNull([NotNull] Mod mod) { diff --git a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index 0d62cc3d37..3cdf25fad0 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -5,13 +5,15 @@ using System; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; namespace osu.Game.Screens.OnlinePlay.Match { + /// + /// A used for free-mod selection in online play. + /// public class FreeModSelectOverlay : ModSelectOverlay { protected override bool AllowCustomisation => false; @@ -29,8 +31,6 @@ namespace osu.Game.Screens.OnlinePlay.Match { } - protected override ModButton CreateModButton(Mod mod) => new FreeModButton(mod); - protected override Drawable CreateHeader(string text) => new Container { AutoSizeAxes = Axes.Y, @@ -47,7 +47,6 @@ namespace osu.Game.Screens.OnlinePlay.Match foreach (var button in ButtonsContainer.OfType()) { if (value) - // Note: Buttons where only part of the group has an implementation are not fully supported. button.SelectAt(0); else button.Deselect(); @@ -77,29 +76,5 @@ namespace osu.Game.Screens.OnlinePlay.Match Changed?.Invoke(value); } } - - private class FreeModButton : ModButton - { - public FreeModButton(Mod mod) - : base(mod) - { - } - - protected override bool OnClick(ClickEvent e) - { - onClick(); - return true; - } - - protected override void OnRightClick(MouseUpEvent e) => onClick(); - - private void onClick() - { - if (Selected) - Deselect(); - else - SelectNext(1); - } - } } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 6367aa54a7..f4136c895f 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -107,7 +107,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (SelectedItem.Value == null) return; - // Remove any extra mods that are no longer allowed. + // Remove any user mods that are no longer allowed. UserMods.Value = UserMods.Value .Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType())) .ToList(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index e6b7656986..08c00e8372 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); } - protected override void OnSetItem(PlaylistItem item) + protected override void SelectItem(PlaylistItem item) { // If the client is already in a room, update via the client. // Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index b75bf93204..46be5591a8 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -76,11 +76,15 @@ namespace osu.Game.Screens.OnlinePlay item.AllowedMods.Clear(); item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy())); - OnSetItem(item); + SelectItem(item); return true; } - protected abstract void OnSetItem(PlaylistItem item); + /// + /// Invoked when the user has requested a selection of a beatmap. + /// + /// The resultant . This item has not yet been added to the 's. + protected abstract void SelectItem(PlaylistItem item); public override bool OnBackButton() { @@ -117,8 +121,18 @@ namespace osu.Game.Screens.OnlinePlay return buttons; } + /// + /// Checks whether a given is valid for global selection. + /// + /// The to check. + /// Whether is a valid mod for online play. protected virtual bool IsValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; + /// + /// Checks whether a given is valid for per-player free-mod selection. + /// + /// The to check. + /// Whether is a selectable free-mod. protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod); } } diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 2d5b07f056..ce1a8a3205 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -26,6 +26,9 @@ namespace osu.Game.Screens.Play.HUD public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; + /// + /// Whether the mods should initially appear expanded, before potentially contracting into their final expansion state (depending on ). + /// public bool ExpandOnAppear = true; private readonly Bindable> current = new Bindable>(); diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 23fe9620fe..1de3e0e989 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Select CreateNewItem = createNewItem }; - protected override void OnSetItem(PlaylistItem item) + protected override void SelectItem(PlaylistItem item) { switch (Playlist.Count) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4af96b7a29..ed6e0a1028 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -300,6 +300,10 @@ namespace osu.Game.Screens.Select } } + /// + /// Creates the buttons to be displayed in the footer. + /// + /// A set of and an optional which the button opens when pressed. protected virtual IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() => new (FooterButton, OverlayContainer)[] { (new FooterButtonMods { Current = Mods }, ModSelect), diff --git a/osu.Game/Utils/ModValidation.cs b/osu.Game/Utils/ModUtils.cs similarity index 79% rename from osu.Game/Utils/ModValidation.cs rename to osu.Game/Utils/ModUtils.cs index 3597396ec4..808dba2900 100644 --- a/osu.Game/Utils/ModValidation.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -12,9 +12,9 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Utils { /// - /// A set of utilities to validate combinations. + /// A set of utilities to handle combinations. /// - public static class ModValidation + public static class ModUtils { /// /// Checks that all s are compatible with each-other, and that all appear within a set of allowed types. @@ -25,11 +25,11 @@ namespace osu.Game.Utils /// The s to check. /// The set of allowed types. /// Whether all s are compatible with each-other and appear in the set of allowed types. - public static bool CheckCompatibleAndAllowed(IEnumerable combination, IEnumerable allowedTypes) + public static bool CheckCompatibleSetAndAllowed(IEnumerable combination, IEnumerable allowedTypes) { // Prevent multiple-enumeration. var combinationList = combination as ICollection ?? combination.ToArray(); - return CheckCompatible(combinationList) && CheckAllowed(combinationList, allowedTypes); + return CheckCompatibleSet(combinationList) && CheckAllowed(combinationList, allowedTypes); } /// @@ -37,7 +37,7 @@ namespace osu.Game.Utils /// /// The combination to check. /// Whether all s in the combination are compatible with each-other. - public static bool CheckCompatible(IEnumerable combination) + public static bool CheckCompatibleSet(IEnumerable combination) { var incompatibleTypes = new HashSet(); var incomingTypes = new HashSet(); @@ -83,20 +83,6 @@ namespace osu.Game.Utils .All(m => allowedSet.Contains(m.GetType())); } - /// - /// Determines whether a is in a set of incompatible types. - /// - /// - /// A can be incompatible through its most-declared type or any of its base types. - /// - /// The to test. - /// The set of incompatible types. - /// Whether the given is incompatible. - private static bool isModIncompatible(Mod mod, ICollection incompatibleTypes) - => FlattenMod(mod) - .SelectMany(m => m.GetType().EnumerateBaseTypes()) - .Any(incompatibleTypes.Contains); - /// /// Flattens a set of s, returning a new set with all s removed. /// From ee92ec0a5c2e6120f45b36f14b9a678ecab78e8a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 18:54:47 +0900 Subject: [PATCH 272/385] Disallow local user mod customisation --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 87ae723c63..f09c7168b3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -216,7 +216,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, Height = 0.5f, - Child = userModsSelectOverlay = new SoloModSelectOverlay + Child = userModsSelectOverlay = new UserModSelectOverlay { SelectedMods = { BindTarget = UserMods }, IsValidMod = _ => false @@ -358,5 +358,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client != null) client.LoadRequested -= onLoadRequested; } + + private class UserModSelectOverlay : ModSelectOverlay + { + protected override bool AllowCustomisation => false; + } } } From 76ebb3811a03ea45fb5279fab26c9951fe199ba9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 19:05:02 +0900 Subject: [PATCH 273/385] Fix mod icons potentially having incorrect colours --- osu.Game/Rulesets/UI/ModIcon.cs | 49 +++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 8ea6c74349..76fcb8e080 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.UI private const float size = 80; - private readonly ModType type; - public virtual string TooltipText => mod.IconTooltip; private Mod mod; @@ -38,16 +36,22 @@ namespace osu.Game.Rulesets.UI set { mod = value; - updateMod(value); + + if (LoadState >= LoadState.Ready) + updateMod(value); } } + [Resolved] + private OsuColour colours { get; set; } + + private Color4 backgroundColour; + private Color4 highlightedColour; + public ModIcon(Mod mod) { this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); - type = mod.Type; - Size = new Vector2(size); Children = new Drawable[] @@ -79,10 +83,20 @@ namespace osu.Game.Rulesets.UI Icon = FontAwesome.Solid.Question }, }; + } + [BackgroundDependencyLoader] + private void load() + { updateMod(mod); } + protected override void LoadComplete() + { + base.LoadComplete(); + Selected.BindValueChanged(_ => updateColour(), true); + } + private void updateMod(Mod value) { modAcronym.Text = value.Acronym; @@ -92,20 +106,14 @@ namespace osu.Game.Rulesets.UI { modIcon.FadeOut(); modAcronym.FadeIn(); - return; + } + else + { + modIcon.FadeIn(); + modAcronym.FadeOut(); } - modIcon.FadeIn(); - modAcronym.FadeOut(); - } - - private Color4 backgroundColour; - private Color4 highlightedColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - switch (type) + switch (value.Type) { default: case ModType.DifficultyIncrease: @@ -139,12 +147,13 @@ namespace osu.Game.Rulesets.UI modIcon.Colour = colours.Yellow; break; } + + updateColour(); } - protected override void LoadComplete() + private void updateColour() { - base.LoadComplete(); - Selected.BindValueChanged(selected => background.Colour = selected.NewValue ? highlightedColour : backgroundColour, true); + background.Colour = Selected.Value ? highlightedColour : backgroundColour; } } } From e5ca9b1e500c06e1cc084bcf4f8d1cc8994a3474 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 19:28:33 +0900 Subject: [PATCH 274/385] Remove usage of removed method --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 3322c8ede1..9e1e9f3d69 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -298,7 +298,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer if (client.Room == null) return; - client.ChangeUserMods(mods.NewValue).CatchUnobservedExceptions(); + client.ChangeUserMods(mods.NewValue); } private void onReadyClick() From b9832c1b2d2e55b06170e67c64c05e2f0cf016fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 19:37:19 +0900 Subject: [PATCH 275/385] Add ModUtils class for validating mod usages --- .../osu.Game.Tests.Android.csproj | 4 + osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj | 1 + osu.Game.Tests/Mods/ModUtilsTest.cs | 64 ++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + osu.Game/Utils/ModUtils.cs | 109 ++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 osu.Game.Tests/Mods/ModUtilsTest.cs create mode 100644 osu.Game/Utils/ModUtils.cs diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj index c44ed69c4d..19e36a63f1 100644 --- a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj +++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj @@ -69,5 +69,9 @@ osu.Game + + + + \ No newline at end of file diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj index ca68369ebb..67b2298f4c 100644 --- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj +++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj @@ -45,6 +45,7 @@ + \ No newline at end of file diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs new file mode 100644 index 0000000000..fdb441343a --- /dev/null +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Moq; +using NUnit.Framework; +using osu.Game.Rulesets.Mods; +using osu.Game.Utils; + +namespace osu.Game.Tests.Mods +{ + [TestFixture] + public class ModUtilsTest + { + [Test] + public void TestModIsCompatibleByItself() + { + var mod = new Mock(); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object })); + } + + [Test] + public void TestIncompatibleThroughTopLevel() + { + var mod1 = new Mock(); + var mod2 = new Mock(); + + mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() }); + + // Test both orderings. + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); + } + + [Test] + public void TestIncompatibleThroughMultiMod() + { + var mod1 = new Mock(); + + // The nested mod. + var mod2 = new Mock(); + mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() }); + + var multiMod = new MultiMod(new MultiMod(mod2.Object)); + + // Test both orderings. + Assert.That(ModUtils.CheckCompatibleSet(new[] { multiMod, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); + } + + [Test] + public void TestAllowedThroughMostDerivedType() + { + var mod = new Mock(); + Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); + } + + [Test] + public void TestNotAllowedThroughBaseType() + { + var mod = new Mock(); + Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index c0c0578391..d29ed94b5f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -7,6 +7,7 @@ + WinExe diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs new file mode 100644 index 0000000000..808dba2900 --- /dev/null +++ b/osu.Game/Utils/ModUtils.cs @@ -0,0 +1,109 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.TypeExtensions; +using osu.Game.Rulesets.Mods; + +#nullable enable + +namespace osu.Game.Utils +{ + /// + /// A set of utilities to handle combinations. + /// + public static class ModUtils + { + /// + /// Checks that all s are compatible with each-other, and that all appear within a set of allowed types. + /// + /// + /// The allowed types must contain exact types for the respective s to be allowed. + /// + /// The s to check. + /// The set of allowed types. + /// Whether all s are compatible with each-other and appear in the set of allowed types. + public static bool CheckCompatibleSetAndAllowed(IEnumerable combination, IEnumerable allowedTypes) + { + // Prevent multiple-enumeration. + var combinationList = combination as ICollection ?? combination.ToArray(); + return CheckCompatibleSet(combinationList) && CheckAllowed(combinationList, allowedTypes); + } + + /// + /// Checks that all s in a combination are compatible with each-other. + /// + /// The combination to check. + /// Whether all s in the combination are compatible with each-other. + public static bool CheckCompatibleSet(IEnumerable combination) + { + var incompatibleTypes = new HashSet(); + var incomingTypes = new HashSet(); + + foreach (var mod in combination.SelectMany(FlattenMod)) + { + // Add the new mod incompatibilities, checking whether any match the existing mod types. + foreach (var t in mod.IncompatibleMods) + { + if (incomingTypes.Contains(t)) + return false; + + incompatibleTypes.Add(t); + } + + // Add the new mod types, checking whether any match the incompatible types. + foreach (var t in mod.GetType().EnumerateBaseTypes()) + { + if (incomingTypes.Contains(t)) + return false; + + incomingTypes.Add(t); + } + } + + return true; + } + + /// + /// Checks that all s in a combination appear within a set of allowed types. + /// + /// + /// The set of allowed types must contain exact types for the respective s to be allowed. + /// + /// The combination to check. + /// The set of allowed types. + /// Whether all s in the combination are allowed. + public static bool CheckAllowed(IEnumerable combination, IEnumerable allowedTypes) + { + var allowedSet = new HashSet(allowedTypes); + + return combination.SelectMany(FlattenMod) + .All(m => allowedSet.Contains(m.GetType())); + } + + /// + /// Flattens a set of s, returning a new set with all s removed. + /// + /// The set of s to flatten. + /// The new set, containing all s in recursively with all s removed. + public static IEnumerable FlattenMods(IEnumerable mods) => mods.SelectMany(FlattenMod); + + /// + /// Flattens a , returning a set of s in-place of any s. + /// + /// The to flatten. + /// A set of singular "flattened" s + public static IEnumerable FlattenMod(Mod mod) + { + if (mod is MultiMod multi) + { + foreach (var m in multi.Mods.SelectMany(FlattenMod)) + yield return m; + } + else + yield return mod; + } + } +} From 97247b7a673fa552b9f8f5b66aa87e9dfe2293c0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 19:59:18 +0900 Subject: [PATCH 276/385] Fix unset key --- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index a25c332b47..d0e19d9f37 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -33,6 +33,7 @@ namespace osu.Game.Online.Multiplayer public IEnumerable Mods { get; set; } = Enumerable.Empty(); [NotNull] + [Key(5)] public IEnumerable AllowedMods { get; set; } = Enumerable.Empty(); public bool Equals(MultiplayerRoomSettings other) From 97e3023df9cb680feedadd089b2eb326078a2e39 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Feb 2021 20:16:58 +0900 Subject: [PATCH 277/385] Renamespace/rename MatchSongSelect -> PlaylistsSongSelect --- .../Visual/Multiplayer/TestSceneMatchSongSelect.cs | 8 ++++---- .../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 3 +-- .../Playlists/PlaylistsSongSelect.cs} | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) rename osu.Game/Screens/{Select/MatchSongSelect.cs => OnlinePlay/Playlists/PlaylistsSongSelect.cs} (91%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs index e0fd7d9874..86429ac50e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs @@ -19,7 +19,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay.Components; -using osu.Game.Screens.Select; +using osu.Game.Screens.OnlinePlay.Playlists; namespace osu.Game.Tests.Visual.Multiplayer { @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; - private TestMatchSongSelect songSelect; + private TestPlaylistsSongSelect songSelect; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Beatmap.SetDefault(); }); - AddStep("create song select", () => LoadScreen(songSelect = new TestMatchSongSelect())); + AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect())); AddUntilStep("wait for present", () => songSelect.IsCurrentScreen()); } @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value)); } - private class TestMatchSongSelect : MatchSongSelect + private class TestPlaylistsSongSelect : PlaylistsSongSelect { public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails; } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 22580f0537..cedde373b3 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; -using osu.Game.Screens.Select; using osu.Game.Users; using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer; @@ -188,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists settingsOverlay = new PlaylistsMatchSettingsOverlay { RelativeSizeAxes = Axes.Both, - EditPlaylist = () => this.Push(new MatchSongSelect()), + EditPlaylist = () => this.Push(new PlaylistsSongSelect()), State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } } }; diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs similarity index 91% rename from osu.Game/Screens/Select/MatchSongSelect.cs rename to osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 1de3e0e989..0e8db6dfe5 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -6,12 +6,12 @@ using osu.Framework.Allocation; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; -using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Screens.Select; -namespace osu.Game.Screens.Select +namespace osu.Game.Screens.OnlinePlay.Playlists { - public class MatchSongSelect : OnlinePlaySongSelect + public class PlaylistsSongSelect : OnlinePlaySongSelect { [Resolved] private BeatmapManager beatmaps { get; set; } From ead8262257ed0749b9b81670aa76b534aa73874f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Feb 2021 20:20:10 +0900 Subject: [PATCH 278/385] Add function to check for (and return) invalid mods --- osu.Game/Utils/ModUtils.cs | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 808dba2900..9e638d4f2f 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -83,6 +83,48 @@ namespace osu.Game.Utils .All(m => allowedSet.Contains(m.GetType())); } + /// + /// Check the provided combination of mods are valid for a local gameplay session. + /// + /// The mods to check. + /// Invalid mods, if any where found. Can be null if all mods were valid. + /// Whether the input mods were all valid. If false, will contain all invalid entries. + public static bool CheckValidForGameplay(IEnumerable mods, out Mod[]? invalidMods) + { + mods = mods.ToArray(); + + List? foundInvalid = null; + + void addInvalid(Mod mod) + { + foundInvalid ??= new List(); + foundInvalid.Add(mod); + } + + foreach (var mod in mods) + { + bool valid = mod.Type != ModType.System + && mod.HasImplementation + && !(mod is MultiMod); + + if (!valid) + { + // if this mod was found as invalid, we can exclude it before potentially excluding more incompatible types. + addInvalid(mod); + continue; + } + + foreach (var type in mod.IncompatibleMods) + { + foreach (var invalid in mods.Where(m => type.IsInstanceOfType(m))) + addInvalid(invalid); + } + } + + invalidMods = foundInvalid?.ToArray(); + return foundInvalid == null; + } + /// /// Flattens a set of s, returning a new set with all s removed. /// From 425dc8a210d69c853c58f4d2a10ce0347914261f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Feb 2021 20:20:19 +0900 Subject: [PATCH 279/385] Ensure mods are always in a valid state at a game level --- osu.Game/OsuGame.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5acd6bc73d..a00cd5e6a0 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -468,6 +468,12 @@ namespace osu.Game private void modsChanged(ValueChangedEvent> mods) { updateModDefaults(); + + if (!ModUtils.CheckValidForGameplay(mods.NewValue, out var invalid)) + { + // ensure we always have a valid set of mods. + SelectedMods.Value = mods.NewValue.Except(invalid).ToArray(); + } } private void updateModDefaults() From 286726feb0cb93c4f3d54e60d690979bba35c005 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Feb 2021 17:44:30 +0000 Subject: [PATCH 280/385] Bump SharpCompress from 0.26.0 to 0.27.1 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.26.0 to 0.27.1. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.26...0.27.1) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1552dff17d..d0aeb10c9c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 48dc01f5de..b40e6a3346 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -89,7 +89,7 @@ - + From 0560676236e2b73eb25726b10a07927912b2b201 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Feb 2021 18:09:05 +0000 Subject: [PATCH 281/385] Bump ppy.osu.Framework.NativeLibs from 2020.923.0 to 2021.115.0 Bumps [ppy.osu.Framework.NativeLibs](https://github.com/ppy/osu-framework) from 2020.923.0 to 2021.115.0. - [Release notes](https://github.com/ppy/osu-framework/releases) - [Commits](https://github.com/ppy/osu-framework/compare/2020.923.0...2021.115.0) Signed-off-by: dependabot-preview[bot] --- osu.iOS.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.iOS.props b/osu.iOS.props index b40e6a3346..dc3527c687 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -93,6 +93,6 @@ - + From 57213e630806a6e25973fa19588009217a0ad9ea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Feb 2021 18:09:07 +0000 Subject: [PATCH 282/385] Bump DiscordRichPresence from 1.0.169 to 1.0.175 Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.0.169 to 1.0.175. - [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases) - [Commits](https://github.com/Lachee/discord-rpc-csharp/commits) Signed-off-by: dependabot-preview[bot] --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index cce7907c6c..3e0f0cb7f6 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -30,7 +30,7 @@ - + From 15fcabb1282c8c36c4f005e6acba36a7d239fb11 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Feb 2021 22:04:44 +0300 Subject: [PATCH 283/385] Add documentation to auto-scroll leniency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/Chat/DrawableChannel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 4c8513b1d5..0cd5ecc05a 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -241,6 +241,11 @@ namespace osu.Game.Overlays.Chat /// private class ChannelScrollContainer : OsuScrollContainer { + /// + /// The chat will be automatically scrolled to end if and only if + /// the distance between the current scroll position and the end of the scroll + /// is less than this value. + /// private const float auto_scroll_leniency = 10f; private float? lastExtent; From 5c28c030c86a270cd6435955ac82de4daa8a196e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Feb 2021 22:08:55 +0300 Subject: [PATCH 284/385] Unconditionally set "autoscroll" state --- osu.Game/Overlays/Chat/DrawableChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 0cd5ecc05a..db6a27bf8c 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -261,8 +261,8 @@ namespace osu.Game.Overlays.Chat if ((lastExtent == null || ScrollableExtent > lastExtent) && ShouldAutoScroll) ScrollToEnd(); - else - ShouldAutoScroll = IsScrolledToEnd(auto_scroll_leniency); + + ShouldAutoScroll = IsScrolledToEnd(auto_scroll_leniency); lastExtent = ScrollableExtent; } From 216b0d89a729cff6ad311ebb440f32dcb1e174e6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Feb 2021 19:16:51 +0000 Subject: [PATCH 285/385] Bump Sentry from 2.1.8 to 3.0.1 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 2.1.8 to 3.0.1. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/2.1.8...3.0.1) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d0aeb10c9c..e2b506e187 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + From dcb1626e4d66649304d9307cdc63329f523192f9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 1 Feb 2021 22:38:42 +0300 Subject: [PATCH 286/385] Remove no longer necessary field --- osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 02ef024128..8ea05784e9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -46,7 +46,6 @@ namespace osu.Game.Tests.Visual.Online private ChannelManager channelManager = new ChannelManager(); private TestStandAloneChatDisplay chatDisplay; - private TestStandAloneChatDisplay chatDisplay2; private int messageIdSequence; private Channel testChannel; @@ -72,7 +71,7 @@ namespace osu.Game.Tests.Visual.Online Size = new Vector2(400, 80), Channel = { Value = testChannel }, }, - chatDisplay2 = new TestStandAloneChatDisplay(true) + new TestStandAloneChatDisplay(true) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, From f166c4c4148553e381f694cbdb7cca3fb65d7cf1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 11:04:09 +0900 Subject: [PATCH 287/385] Rename test --- ...tSceneMatchSongSelect.cs => TestScenePlaylistsSongSelect.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneMatchSongSelect.cs => TestScenePlaylistsSongSelect.cs} (99%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs similarity index 99% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs rename to osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs index 86429ac50e..2f7e59f800 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsSongSelect.cs @@ -23,7 +23,7 @@ using osu.Game.Screens.OnlinePlay.Playlists; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneMatchSongSelect : RoomTestScene + public class TestScenePlaylistsSongSelect : RoomTestScene { [Resolved] private BeatmapManager beatmapManager { get; set; } From 4cf52077b6dbb51bfb48f4589ed88f47cab886ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 11:11:28 +0900 Subject: [PATCH 288/385] Make checkbox also respond to all mods selected --- osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index 3cdf25fad0..9a3c87f1ff 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -57,12 +57,8 @@ namespace osu.Game.Screens.OnlinePlay.Match { base.Update(); - // If any of the buttons aren't selected, deselect the checkbox. - foreach (var button in ButtonsContainer.OfType()) - { - if (button.Mods.Any(m => m.HasImplementation) && !button.Selected) - checkbox.Current.Value = false; - } + var validButtons = ButtonsContainer.OfType().Where(b => b.Mod.HasImplementation); + checkbox.Current.Value = validButtons.All(b => b.Selected); } } From b54f65c28279ffcc4d53b9eebdac6c1b73ac4d30 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 12:48:15 +0900 Subject: [PATCH 289/385] Exclude more mods from multiplayer --- .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 08c00e8372..f7f0402555 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -68,6 +68,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust); + protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust) && !mod.RequiresConfiguration; } } diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 46be5591a8..755dbdb55e 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -126,7 +126,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; /// /// Checks whether a given is valid for per-player free-mod selection. From 7a14e14e67db572f8ec1c8154d6e2e0111a0429b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 12:49:49 +0900 Subject: [PATCH 290/385] Refactor condition This won't make any noticeable difference, but is the more correct way to handle MultiMod because flattening works through infinite recursion levels. --- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 755dbdb55e..b0062720e0 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.Select; +using osu.Game.Utils; namespace osu.Game.Screens.OnlinePlay { @@ -126,7 +127,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && !ModUtils.FlattenMod(mod).Any(m => m is ModAutoplay); /// /// Checks whether a given is valid for per-player free-mod selection. From 0d5353008c1d3e9520d7d12bf50d6f23b85d9712 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 13:34:34 +0900 Subject: [PATCH 291/385] Update sentry sdk usage --- osu.Game/Utils/SentryLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index e8e41cdbbe..be9d01cde6 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -23,7 +23,7 @@ namespace osu.Game.Utils var options = new SentryOptions { - Dsn = new Dsn("https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255"), + Dsn = "https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255", Release = game.Version }; From 9c3c0895cf2a63d3a7517d53fecf8a0d64c149eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:03:46 +0900 Subject: [PATCH 292/385] Hide customise button + multiplier label --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 25cb75f73a..a7bfac4088 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.Mods protected readonly TriangleButton CustomiseButton; protected readonly TriangleButton CloseButton; + protected readonly Drawable MultiplierSection; protected readonly OsuSpriteText MultiplierLabel; /// @@ -316,7 +317,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, - new FillFlowContainer + MultiplierSection = new FillFlowContainer { AutoSizeAxes = Axes.Both, Spacing = new Vector2(footer_button_spacing / 2, 0), diff --git a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index 9a3c87f1ff..f22c6603e5 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -20,6 +20,12 @@ namespace osu.Game.Screens.OnlinePlay.Match protected override bool Stacked => false; + public FreeModSelectOverlay() + { + CustomiseButton.Alpha = 0; + MultiplierSection.Alpha = 0; + } + protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); private class FreeModSection : ModSection From 87f9e46b164c540d9f7a531510521087f3840a0c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:37:25 +0900 Subject: [PATCH 293/385] Add option to select all --- osu.Game/Overlays/Mods/ModSection.cs | 8 +++- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 13 +++--- .../OnlinePlay/Match/FreeModSelectOverlay.cs | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 5de629424b..993f4ef9d7 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -91,7 +91,13 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } - public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); + public void SelectAll() + { + foreach (var button in buttons.Where(b => !b.Selected)) + button.SelectAt(0); + } + + public void DeselectAll(bool immediate = false) => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null), immediate); /// /// Deselect one or more mods in this section. diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a7bfac4088..087990d3f8 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -33,6 +33,7 @@ namespace osu.Game.Overlays.Mods { public const float HEIGHT = 510; + protected readonly FillFlowContainer FooterContainer; protected readonly TriangleButton DeselectAllButton; protected readonly TriangleButton CustomiseButton; protected readonly TriangleButton CloseButton; @@ -85,8 +86,6 @@ namespace osu.Game.Overlays.Mods private const float content_width = 0.8f; private const float footer_button_spacing = 20; - private readonly FillFlowContainer footerContainer; - private SampleChannel sampleOn, sampleOff; protected ModSelectOverlay() @@ -275,7 +274,7 @@ namespace osu.Game.Overlays.Mods Colour = new Color4(172, 20, 116, 255), Alpha = 0.5f, }, - footerContainer = new FillFlowContainer + FooterContainer = new FillFlowContainer { Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, @@ -385,8 +384,8 @@ namespace osu.Game.Overlays.Mods { base.PopOut(); - footerContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); foreach (var section in ModSectionsContainer.Children) { @@ -400,8 +399,8 @@ namespace osu.Game.Overlays.Mods { base.PopIn(); - footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + FooterContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + FooterContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); foreach (var section in ModSectionsContainer.Children) { diff --git a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs index f22c6603e5..9a676930a0 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/FreeModSelectOverlay.cs @@ -24,6 +24,46 @@ namespace osu.Game.Screens.OnlinePlay.Match { CustomiseButton.Alpha = 0; MultiplierSection.Alpha = 0; + DeselectAllButton.Alpha = 0; + + Drawable selectAllButton; + Drawable deselectAllButton; + + FooterContainer.AddRange(new[] + { + selectAllButton = new TriangleButton + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Width = 180, + Text = "Select All", + Action = selectAll, + }, + // Unlike the base mod select overlay, this button deselects mods instantaneously. + deselectAllButton = new TriangleButton + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Width = 180, + Text = "Deselect All", + Action = deselectAll, + }, + }); + + FooterContainer.SetLayoutPosition(selectAllButton, -2); + FooterContainer.SetLayoutPosition(deselectAllButton, -1); + } + + private void selectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.SelectAll(); + } + + private void deselectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.DeselectAll(true); } protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); From 1d3dff8c75988867dce9fd6124fdb08bcb84e344 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:41:01 +0900 Subject: [PATCH 294/385] Refactor ModDisplay flag usage --- osu.Game/Screens/Play/HUD/ModDisplay.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index ce1a8a3205..052484afc4 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -113,14 +113,10 @@ namespace osu.Game.Screens.Play.HUD else unrankedText.Hide(); - if (ExpandOnAppear) - { - expand(); - using (iconsContainer.BeginDelayedSequence(1200)) - contract(); - } - else - iconsContainer.TransformSpacingTo(new Vector2(-25, 0)); + expand(); + + using (iconsContainer.BeginDelayedSequence(ExpandOnAppear ? 1200 : 0)) + contract(); } private void expand() From 53cfc3bc6ea4ddda9b8faf32a97b954e5a09c4f9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:42:45 +0900 Subject: [PATCH 295/385] Make ModIcon a bit more safe --- osu.Game/Rulesets/UI/ModIcon.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 981fe7f4a6..2ff59f4d1a 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.UI { mod = value; - if (LoadState >= LoadState.Ready) + if (IsLoaded) updateMod(value); } } @@ -98,13 +98,15 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load() { - updateMod(mod); } protected override void LoadComplete() { base.LoadComplete(); - Selected.BindValueChanged(_ => updateColour(), true); + + Selected.BindValueChanged(_ => updateColour()); + + updateMod(mod); } private void updateMod(Mod value) From 173e20938cd0b87fc47747cc13b8deba60bb9bea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:49:58 +0900 Subject: [PATCH 296/385] Revert changes to ModDisplay --- .../Multiplayer/Participants/ParticipantPanel.cs | 1 - osu.Game/Screens/Play/HUD/ModDisplay.cs | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 8b907066a8..8036e5f702 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -139,7 +139,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants Scale = new Vector2(0.5f), ExpansionMode = ExpansionMode.AlwaysContracted, DisplayUnrankedText = false, - ExpandOnAppear = false } }, userStateDisplay = new StateDisplay diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 052484afc4..68d019bf71 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -26,11 +26,6 @@ namespace osu.Game.Screens.Play.HUD public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; - /// - /// Whether the mods should initially appear expanded, before potentially contracting into their final expansion state (depending on ). - /// - public bool ExpandOnAppear = true; - private readonly Bindable> current = new Bindable>(); public Bindable> Current @@ -115,7 +110,7 @@ namespace osu.Game.Screens.Play.HUD expand(); - using (iconsContainer.BeginDelayedSequence(ExpandOnAppear ? 1200 : 0)) + using (iconsContainer.BeginDelayedSequence(1200)) contract(); } From 4194c9308eedc24e5ad6cc6315eb945e0838a7a8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:50:05 +0900 Subject: [PATCH 297/385] Add xmldoc --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 087990d3f8..b20c2d9d19 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -288,7 +288,7 @@ namespace osu.Game.Overlays.Mods Vertical = 15, Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] + Children = new[] { DeselectAllButton = new TriangleButton { @@ -509,6 +509,10 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } + /// + /// Invoked when a new has been selected. + /// + /// The that has been selected. protected virtual void OnModSelected(Mod mod) { } From 0bce9d68335d78f9c9286af6520e330f3d3f5c59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 13:54:27 +0900 Subject: [PATCH 298/385] Clear freemods when ruleset is changed --- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index b0062720e0..c58632c500 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -60,6 +60,13 @@ namespace osu.Game.Screens.OnlinePlay freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); FooterPanels.Add(freeModSelectOverlay); + + Ruleset.BindValueChanged(onRulesetChanged); + } + + private void onRulesetChanged(ValueChangedEvent ruleset) + { + freeMods.Value = Array.Empty(); } protected sealed override bool OnStart() From 80d88024d6bbca6db1f9ca2765340bb765f5ae07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 14:13:50 +0900 Subject: [PATCH 299/385] Add basic test coverage of CheckValidForGameplay function --- osu.Game.Tests/Mods/ModUtilsTest.cs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index fdb441343a..88eee5449c 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -1,9 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; using Moq; using NUnit.Framework; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Utils; namespace osu.Game.Tests.Mods @@ -60,5 +64,29 @@ namespace osu.Game.Tests.Mods var mod = new Mock(); Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); } + + // test incompatible pair. + [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) }, new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) })] + // test incompatible pair with derived class. + [TestCase(new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) }, new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) })] + // test system mod. + [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModTouchDevice) }, new[] { typeof(OsuModTouchDevice) })] + // test valid. + [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModHardRock) }, null)] + public void TestInvalidModScenarios(Type[] input, Type[] expectedInvalid) + { + List inputMods = new List(); + foreach (var t in input) + inputMods.Add((Mod)Activator.CreateInstance(t)); + + bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid); + + Assert.That(isValid, Is.EqualTo(expectedInvalid == null)); + + if (isValid) + Assert.IsNull(invalid); + else + Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); + } } } From 1c645601d41491004c96903231c61ce5a163c7b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 14:14:31 +0900 Subject: [PATCH 300/385] Fix typo in xmldoc --- osu.Game/Utils/ModUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 9e638d4f2f..2146abacb6 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -87,7 +87,7 @@ namespace osu.Game.Utils /// Check the provided combination of mods are valid for a local gameplay session. /// /// The mods to check. - /// Invalid mods, if any where found. Can be null if all mods were valid. + /// Invalid mods, if any were found. Can be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. public static bool CheckValidForGameplay(IEnumerable mods, out Mod[]? invalidMods) { From ed63b571d2185d4b3e86d77a08c0f9ce656f819c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:16:26 +0900 Subject: [PATCH 301/385] Add "new" override for ScrollToEnd To UserTrackingScrollContainer --- osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs index b8ce34b204..be33c231c9 100644 --- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -45,5 +45,11 @@ namespace osu.Game.Graphics.Containers UserScrolling = false; base.ScrollTo(value, animated, distanceDecay); } + + public new void ScrollToEnd(bool animated = true, bool allowDuringDrag = false) + { + UserScrolling = false; + base.ScrollToEnd(animated, allowDuringDrag); + } } } From 398ab9c2c2283a8bad2dd2bf513103a606868e89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:16:10 +0900 Subject: [PATCH 302/385] Use UserTrackingScrollContainer instead --- .../Online/TestSceneStandAloneChatDisplay.cs | 2 +- osu.Game/Overlays/Chat/DrawableChannel.cs | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 8ea05784e9..e7669262fe 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -208,7 +208,7 @@ namespace osu.Game.Tests.Visual.Online protected DrawableChannel DrawableChannel => InternalChildren.OfType().First(); - protected OsuScrollContainer ScrollContainer => (OsuScrollContainer)((Container)DrawableChannel.Child).Child; + protected UserTrackingScrollContainer ScrollContainer => (UserTrackingScrollContainer)((Container)DrawableChannel.Child).Child; public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child; diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index db6a27bf8c..1d021b331a 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Chat @@ -147,8 +148,8 @@ namespace osu.Game.Overlays.Chat // due to the scroll adjusts from old messages removal above, a scroll-to-end must be enforced, // to avoid making the container think the user has scrolled back up and unwantedly disable auto-scrolling. - if (scroll.ShouldAutoScroll || newMessages.Any(m => m is LocalMessage)) - ScheduleAfterChildren(() => scroll.ScrollToEnd()); + if (newMessages.Any(m => m is LocalMessage)) + scroll.ScrollToEnd(); }); private void pendingMessageResolved(Message existing, Message updated) => Schedule(() => @@ -239,7 +240,7 @@ namespace osu.Game.Overlays.Chat /// /// An with functionality to automatically scroll whenever the maximum scrollable distance increases. /// - private class ChannelScrollContainer : OsuScrollContainer + private class ChannelScrollContainer : UserTrackingScrollContainer { /// /// The chat will be automatically scrolled to end if and only if @@ -250,21 +251,30 @@ namespace osu.Game.Overlays.Chat private float? lastExtent; - /// - /// Whether this container should automatically scroll to end on the next call to . - /// - public bool ShouldAutoScroll { get; private set; } = true; + protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default) + { + base.OnUserScroll(value, animated, distanceDecay); + lastExtent = null; + } protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - if ((lastExtent == null || ScrollableExtent > lastExtent) && ShouldAutoScroll) - ScrollToEnd(); + // If the user has scrolled to the bottom of the container, we should resume tracking new content. + bool cancelUserScroll = UserScrolling && IsScrolledToEnd(auto_scroll_leniency); - ShouldAutoScroll = IsScrolledToEnd(auto_scroll_leniency); + // If the user hasn't overridden our behaviour and there has been new content added to the container, we should update our scroll position to track it. + bool requiresScrollUpdate = !UserScrolling && (lastExtent == null || Precision.AlmostBigger(ScrollableExtent, lastExtent.Value)); - lastExtent = ScrollableExtent; + if (cancelUserScroll || requiresScrollUpdate) + { + ScheduleAfterChildren(() => + { + ScrollToEnd(); + lastExtent = ScrollableExtent; + }); + } } } } From bb0753f68d79860b0631938dbbea7b9cd9ab2210 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:44:03 +0900 Subject: [PATCH 303/385] Use a better method of cancelling user scroll --- .../Containers/UserTrackingScrollContainer.cs | 2 ++ osu.Game/Overlays/Chat/DrawableChannel.cs | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs index be33c231c9..17506ce0f5 100644 --- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -25,6 +25,8 @@ namespace osu.Game.Graphics.Containers /// public bool UserScrolling { get; private set; } + public void CancelUserScroll() => UserScrolling = false; + public UserTrackingScrollContainer() { } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 1d021b331a..de3057e9dc 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -262,17 +262,21 @@ namespace osu.Game.Overlays.Chat base.UpdateAfterChildren(); // If the user has scrolled to the bottom of the container, we should resume tracking new content. - bool cancelUserScroll = UserScrolling && IsScrolledToEnd(auto_scroll_leniency); + if (UserScrolling && IsScrolledToEnd(auto_scroll_leniency)) + CancelUserScroll(); // If the user hasn't overridden our behaviour and there has been new content added to the container, we should update our scroll position to track it. bool requiresScrollUpdate = !UserScrolling && (lastExtent == null || Precision.AlmostBigger(ScrollableExtent, lastExtent.Value)); - if (cancelUserScroll || requiresScrollUpdate) + if (requiresScrollUpdate) { ScheduleAfterChildren(() => { - ScrollToEnd(); - lastExtent = ScrollableExtent; + if (!UserScrolling) + { + ScrollToEnd(); + lastExtent = ScrollableExtent; + } }); } } From 3670bd40c2c8dee50cdbc4ab3a61cf412c603b15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:44:11 +0900 Subject: [PATCH 304/385] Add test coverage of user scroll overriding --- .../Online/TestSceneStandAloneChatDisplay.cs | 82 +++++++++++++++---- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index e7669262fe..0e1c90f88e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -12,10 +12,11 @@ using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Chat; +using osuTK.Input; namespace osu.Game.Tests.Visual.Online { - public class TestSceneStandAloneChatDisplay : OsuTestScene + public class TestSceneStandAloneChatDisplay : OsuManualInputManagerTestScene { private readonly User admin = new User { @@ -128,7 +129,7 @@ namespace osu.Game.Tests.Visual.Online Timestamp = DateTimeOffset.Now })); - AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + checkScrolledToBottom(); const int messages_per_call = 10; AddRepeatStep("add many messages", () => @@ -157,7 +158,7 @@ namespace osu.Game.Tests.Visual.Online return true; }); - AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); + checkScrolledToBottom(); } /// @@ -165,6 +166,58 @@ namespace osu.Game.Tests.Visual.Online /// [Test] public void TestMessageWrappingKeepsAutoScrolling() + { + fillChat(); + + // send message with short words for text wrapping to occur when contracting chat. + sendMessage(); + + AddStep("contract chat", () => chatDisplay.Width -= 100); + checkScrolledToBottom(); + + AddStep("send another message", () => testChannel.AddNewMessages(new Message(messageIdSequence++) + { + Sender = admin, + Content = "As we were saying...", + })); + + checkScrolledToBottom(); + } + + [Test] + public void TestUserScrollOverride() + { + fillChat(); + + sendMessage(); + checkScrolledToBottom(); + + AddStep("User scroll up", () => + { + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre + new Vector2(0, chatDisplay.ScreenSpaceDrawQuad.Height)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + checkNotScrolledToBottom(); + sendMessage(); + checkNotScrolledToBottom(); + + AddRepeatStep("User scroll to bottom", () => + { + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre - new Vector2(0, chatDisplay.ScreenSpaceDrawQuad.Height)); + InputManager.ReleaseButton(MouseButton.Left); + }, 5); + + checkScrolledToBottom(); + sendMessage(); + checkScrolledToBottom(); + } + + private void fillChat() { AddStep("fill chat", () => { @@ -178,27 +231,24 @@ namespace osu.Game.Tests.Visual.Online } }); - AddAssert("ensure scrolled to bottom", () => chatDisplay.ScrolledToBottom); + checkScrolledToBottom(); + } - // send message with short words for text wrapping to occur when contracting chat. + private void sendMessage() + { AddStep("send lorem ipsum", () => testChannel.AddNewMessages(new Message(messageIdSequence++) { Sender = longUsernameUser, Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce et bibendum velit.", })); - - AddStep("contract chat", () => chatDisplay.Width -= 100); - AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); - - AddStep("send another message", () => testChannel.AddNewMessages(new Message(messageIdSequence++) - { - Sender = admin, - Content = "As we were saying...", - })); - - AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom); } + private void checkScrolledToBottom() => + AddUntilStep("is scrolled to bottom", () => chatDisplay.ScrolledToBottom); + + private void checkNotScrolledToBottom() => + AddUntilStep("not scrolled to bottom", () => !chatDisplay.ScrolledToBottom); + private class TestStandAloneChatDisplay : StandAloneChatDisplay { public TestStandAloneChatDisplay(bool textbox = false) From b3105fb2920ac3f3c24e144550cbc598bbcf0cf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:46:26 +0900 Subject: [PATCH 305/385] Add coverage of local echo messages performing automatic scrolling --- .../Online/TestSceneStandAloneChatDisplay.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 0e1c90f88e..ee01eb5f3a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -217,6 +217,33 @@ namespace osu.Game.Tests.Visual.Online checkScrolledToBottom(); } + [Test] + public void TestLocalEchoMessageResetsScroll() + { + fillChat(); + + sendMessage(); + checkScrolledToBottom(); + + AddStep("User scroll up", () => + { + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(chatDisplay.ScreenSpaceDrawQuad.Centre + new Vector2(0, chatDisplay.ScreenSpaceDrawQuad.Height)); + InputManager.ReleaseButton(MouseButton.Left); + }); + + checkNotScrolledToBottom(); + sendMessage(); + checkNotScrolledToBottom(); + + sendLocalMessage(); + checkScrolledToBottom(); + + sendMessage(); + checkScrolledToBottom(); + } + private void fillChat() { AddStep("fill chat", () => @@ -243,6 +270,15 @@ namespace osu.Game.Tests.Visual.Online })); } + private void sendLocalMessage() + { + AddStep("send local echo", () => testChannel.AddLocalEcho(new LocalEchoMessage() + { + Sender = longUsernameUser, + Content = "This is a local echo message.", + })); + } + private void checkScrolledToBottom() => AddUntilStep("is scrolled to bottom", () => chatDisplay.ScrolledToBottom); From a76314a8760f98474067decfa23c8ed980def836 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 15:57:17 +0900 Subject: [PATCH 306/385] Use Update instead of UpdateAfterChildren (no need for the latter) --- osu.Game/Overlays/Chat/DrawableChannel.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index de3057e9dc..86ce724390 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -257,9 +257,9 @@ namespace osu.Game.Overlays.Chat lastExtent = null; } - protected override void UpdateAfterChildren() + protected override void Update() { - base.UpdateAfterChildren(); + base.Update(); // If the user has scrolled to the bottom of the container, we should resume tracking new content. if (UserScrolling && IsScrolledToEnd(auto_scroll_leniency)) @@ -270,7 +270,8 @@ namespace osu.Game.Overlays.Chat if (requiresScrollUpdate) { - ScheduleAfterChildren(() => + // Schedule required to allow FillFlow to be the correct size. + Schedule(() => { if (!UserScrolling) { From 54c0bdf7d3174ec1b6ee5ba61abf096492d7fa2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:04:42 +0900 Subject: [PATCH 307/385] Fix PlaylistLoungeTestScene appearing very narrow --- .../Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 008c862cc3..730bbbb397 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -28,12 +28,7 @@ namespace osu.Game.Tests.Visual.Playlists { base.SetUpSteps(); - AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Width = 0.5f, - })); + AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen())); AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen()); } From 3002fef05eebf7c7102ff2e07838347b232d4749 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:11:13 +0900 Subject: [PATCH 308/385] Remove empty parenthesis --- osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index ee01eb5f3a..01e67b1681 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Online private void sendLocalMessage() { - AddStep("send local echo", () => testChannel.AddLocalEcho(new LocalEchoMessage() + AddStep("send local echo", () => testChannel.AddLocalEcho(new LocalEchoMessage { Sender = longUsernameUser, Content = "This is a local echo message.", From 43052991f8120b51b6cce28f68f07f9250b09b47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:18:55 +0900 Subject: [PATCH 309/385] Remove unused using statement --- .../Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 730bbbb397..618447eae2 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -4,7 +4,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics.Containers; From bdc05af4b7d3fcf43d919c594991f679410ae18d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:30:45 +0900 Subject: [PATCH 310/385] Make playlist settings area taller to better match screen aspect ratio --- .../OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs index 01f9920609..ced6d1c5db 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsMatchSettingsOverlay.cs @@ -200,7 +200,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Child = new GridContainer { RelativeSizeAxes = Axes.X, - Height = 300, + Height = 500, Content = new[] { new Drawable[] From fb52ac8c69792e0101c7f612904cb562b3d1e8c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:57:08 +0900 Subject: [PATCH 311/385] Share remove from playlist button design with adjacent download button --- .../Graphics/UserInterface/DownloadButton.cs | 60 +++++++------------ osu.Game/Graphics/UserInterface/GrayButton.cs | 48 +++++++++++++++ .../OnlinePlay/DrawableRoomPlaylistItem.cs | 26 +++++++- 3 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/GrayButton.cs diff --git a/osu.Game/Graphics/UserInterface/DownloadButton.cs b/osu.Game/Graphics/UserInterface/DownloadButton.cs index 5168ff646b..7a8db158c1 100644 --- a/osu.Game/Graphics/UserInterface/DownloadButton.cs +++ b/osu.Game/Graphics/UserInterface/DownloadButton.cs @@ -4,54 +4,38 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Online; using osuTK; namespace osu.Game.Graphics.UserInterface { - public class DownloadButton : OsuAnimatedButton + public class DownloadButton : GrayButton { - public readonly Bindable State = new Bindable(); - - private readonly SpriteIcon icon; - private readonly SpriteIcon checkmark; - private readonly Box background; - [Resolved] private OsuColour colours { get; set; } + public readonly Bindable State = new Bindable(); + + private SpriteIcon checkmark; + public DownloadButton() + : base(FontAwesome.Solid.Download) { - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - Depth = float.MaxValue - }, - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(13), - Icon = FontAwesome.Solid.Download, - }, - checkmark = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - X = 8, - Size = Vector2.Zero, - Icon = FontAwesome.Solid.Check, - } - }; } [BackgroundDependencyLoader] private void load() { + AddInternal(checkmark = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = 8, + Size = Vector2.Zero, + Icon = FontAwesome.Solid.Check, + }); + State.BindValueChanged(updateState, true); } @@ -60,27 +44,27 @@ namespace osu.Game.Graphics.UserInterface switch (state.NewValue) { case DownloadState.NotDownloaded: - background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); - icon.MoveToX(0, 500, Easing.InOutExpo); + Background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); + Icon.MoveToX(0, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); TooltipText = "Download"; break; case DownloadState.Downloading: - background.FadeColour(colours.Blue, 500, Easing.InOutExpo); - icon.MoveToX(0, 500, Easing.InOutExpo); + Background.FadeColour(colours.Blue, 500, Easing.InOutExpo); + Icon.MoveToX(0, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); TooltipText = "Downloading..."; break; case DownloadState.Importing: - background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); + Background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); TooltipText = "Importing"; break; case DownloadState.LocallyAvailable: - background.FadeColour(colours.Green, 500, Easing.InOutExpo); - icon.MoveToX(-8, 500, Easing.InOutExpo); + Background.FadeColour(colours.Green, 500, Easing.InOutExpo); + Icon.MoveToX(-8, 500, Easing.InOutExpo); checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); break; } diff --git a/osu.Game/Graphics/UserInterface/GrayButton.cs b/osu.Game/Graphics/UserInterface/GrayButton.cs new file mode 100644 index 0000000000..dd05701545 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/GrayButton.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class GrayButton : OsuAnimatedButton + { + protected SpriteIcon Icon; + protected Box Background; + + private readonly IconUsage icon; + + [Resolved] + private OsuColour colours { get; set; } + + public GrayButton(IconUsage icon) + { + this.icon = icon; + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + Background = new Box + { + Colour = colours.Gray4, + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }, + Icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(13), + Icon = icon, + }, + }; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index f8982582d5..4316a9508e 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -214,7 +214,8 @@ namespace osu.Game.Screens.OnlinePlay Origin = Anchor.CentreRight, Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, - X = -18, + Spacing = new Vector2(5), + X = -10, ChildrenEnumerable = CreateButtons() } } @@ -225,16 +226,35 @@ namespace osu.Game.Screens.OnlinePlay { new PlaylistDownloadButton(Item) { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Size = new Vector2(50, 30) }, - new IconButton + new PlaylistRemoveButton { - Icon = FontAwesome.Solid.MinusSquare, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(30, 30), Alpha = allowEdit ? 1 : 0, Action = () => RequestDeletion?.Invoke(Model), }, }; + public class PlaylistRemoveButton : GrayButton + { + public PlaylistRemoveButton() + : base(FontAwesome.Solid.MinusSquare) + { + TooltipText = "Remove from playlist"; + } + + [BackgroundDependencyLoader] + private void load() + { + Icon.Scale = new Vector2(0.8f); + } + } + protected override bool OnClick(ClickEvent e) { if (allowSelection) From 6d9ac4d0f01bfefccd38b399ca9158bd4415ce23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 16:57:27 +0900 Subject: [PATCH 312/385] Increase darkness of gradient on buttons to make text readability (slightly) better --- .../TestSceneDrawableRoomPlaylist.cs | 17 ++++++++++++++++- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 14 ++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 874c1694eb..16f6723e2d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.OnlinePlay; using osu.Game.Tests.Beatmaps; +using osu.Game.Users; using osuTK; using osuTK.Input; @@ -278,7 +279,21 @@ namespace osu.Game.Tests.Visual.Multiplayer playlist.Items.Add(new PlaylistItem { ID = i, - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Beatmap = + { + Value = i % 2 == 1 + ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo + : new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "Artist", + Author = new User { Username = "Creator name here" }, + Title = "Long title used to check background colour", + }, + BeatmapSet = new BeatmapSetInfo() + } + }, Ruleset = { Value = new OsuRuleset().RulesetInfo }, RequiredMods = { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 4316a9508e..844758b262 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -342,20 +342,14 @@ namespace osu.Game.Screens.OnlinePlay new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.9f)), - Width = 0.05f, + Colour = ColourInfo.GradientHorizontal(Color4.Black, new Color4(0f, 0f, 0f, 0.7f)), + Width = 0.4f, }, new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)), - Width = 0.2f, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)), - Width = 0.05f, + Colour = ColourInfo.GradientHorizontal(new Color4(0f, 0f, 0f, 0.7f), new Color4(0, 0, 0, 0.4f)), + Width = 0.4f, }, } } From 40233fb47cba94b9b48eafdf28944036c301e347 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 17:09:59 +0900 Subject: [PATCH 313/385] Make font bolder --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 155 +++++++++--------- 1 file changed, 80 insertions(+), 75 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 844758b262..a7015ba1c4 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -124,102 +124,107 @@ namespace osu.Game.Screens.OnlinePlay modDisplay.Current.Value = requiredMods.ToArray(); } - protected override Drawable CreateContent() => maskingContainer = new Container + protected override Drawable CreateContent() { - RelativeSizeAxes = Axes.X, - Height = 50, - Masking = true, - CornerRadius = 10, - Children = new Drawable[] + Action fontParameters = s => s.Font = OsuFont.Default.With(weight: FontWeight.SemiBold); + + return maskingContainer = new Container { - new Box // A transparent box that forces the border to be drawn if the panel background is opaque + RelativeSizeAxes = Axes.X, + Height = 50, + Masking = true, + CornerRadius = 10, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - }, - new PanelBackground - { - RelativeSizeAxes = Axes.Both, - Beatmap = { BindTarget = beatmap } - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 8 }, - Spacing = new Vector2(8, 0), - Direction = FillDirection.Horizontal, - Children = new Drawable[] + new Box // A transparent box that forces the border to be drawn if the panel background is opaque { - difficultyIconContainer = new Container + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }, + new PanelBackground + { + RelativeSizeAxes = Axes.Both, + Beatmap = { BindTarget = beatmap } + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 8 }, + Spacing = new Vector2(8, 0), + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + difficultyIconContainer = new Container { - beatmapText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, - new FillFlowContainer + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + beatmapText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, + new FillFlowContainer { - new FillFlowContainer + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10f, 0), - Children = new Drawable[] + new FillFlowContainer { - authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both }, - explicitContentPill = new ExplicitContentBeatmapPill + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0), + Children = new Drawable[] { - Alpha = 0f, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Top = 3f }, - } + authorText = new LinkFlowContainer(fontParameters) { AutoSizeAxes = Axes.Both }, + explicitContentPill = new ExplicitContentBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Top = 3f }, + } + }, }, - }, - new Container - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Child = modDisplay = new ModDisplay + new Container { - Scale = new Vector2(0.4f), - DisplayUnrankedText = false, - ExpansionMode = ExpansionMode.AlwaysExpanded + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Child = modDisplay = new ModDisplay + { + Scale = new Vector2(0.4f), + DisplayUnrankedText = false, + ExpansionMode = ExpansionMode.AlwaysExpanded + } } } } } } } + }, + new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + X = -10, + ChildrenEnumerable = CreateButtons() } - }, - new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - X = -10, - ChildrenEnumerable = CreateButtons() } - } - }; + }; + } protected virtual IEnumerable CreateButtons() => new Drawable[] From bc8a4f411159140371596546144fd56e34bc63d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 17:21:46 +0900 Subject: [PATCH 314/385] Update test handling --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 16f6723e2d..960aad10c6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -11,8 +11,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets; @@ -242,7 +242,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } private void moveToItem(int index, Vector2? offset = null) - => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType>().ElementAt(index), offset)); + => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); private void moveToDragger(int index, Vector2? offset = null) => AddStep($"move mouse to dragger {index}", () => { @@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void moveToDeleteButton(int index, Vector2? offset = null) => AddStep($"move mouse to delete button {index}", () => { var item = playlist.ChildrenOfType>().ElementAt(index); - InputManager.MoveMouseTo(item.ChildrenOfType().ElementAt(0), offset); + InputManager.MoveMouseTo(item.ChildrenOfType().ElementAt(0), offset); }); private void assertHandleVisibility(int index, bool visible) @@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Multiplayer () => (playlist.ChildrenOfType.PlaylistItemHandle>().ElementAt(index).Alpha > 0) == visible); private void assertDeleteButtonVisibility(int index, bool visible) - => AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible", () => (playlist.ChildrenOfType().ElementAt(2 + index * 2).Alpha > 0) == visible); + => AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible", () => (playlist.ChildrenOfType().ElementAt(2 + index * 2).Alpha > 0) == visible); private void createPlaylist(bool allowEdit, bool allowSelection) { From 7c29386717cc7f8c96668dc9fb4d970c7b43a17a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:01:33 +0900 Subject: [PATCH 315/385] Add failing tests --- osu.Game.Tests/Mods/ModUtilsTest.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index fdb441343a..b602c082bf 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -47,6 +47,29 @@ namespace osu.Game.Tests.Mods Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); } + [Test] + public void TestCompatibleMods() + { + var mod1 = new Mock(); + var mod2 = new Mock(); + + // Test both orderings. + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.True); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.True); + } + + [Test] + public void TestIncompatibleThroughBaseType() + { + var mod1 = new Mock(); + var mod2 = new Mock(); + mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType().BaseType }); + + // Test both orderings. + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); + } + [Test] public void TestAllowedThroughMostDerivedType() { From 8232d9d2fe667a201aded46f4df1dba44b93ae7e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:01:38 +0900 Subject: [PATCH 316/385] Fix incorrect implementation --- osu.Game/Utils/ModUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 808dba2900..34bc0faca4 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -56,7 +56,7 @@ namespace osu.Game.Utils // Add the new mod types, checking whether any match the incompatible types. foreach (var t in mod.GetType().EnumerateBaseTypes()) { - if (incomingTypes.Contains(t)) + if (incompatibleTypes.Contains(t)) return false; incomingTypes.Add(t); From d0655c21c6db7e73b006a837b276d2efd0492e27 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:18:57 +0900 Subject: [PATCH 317/385] Simplify implementation of CheckCompatibleSet --- osu.Game/Utils/ModUtils.cs | 40 ++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 34bc0faca4..a9271db1b5 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions.TypeExtensions; using osu.Game.Rulesets.Mods; #nullable enable @@ -29,7 +28,7 @@ namespace osu.Game.Utils { // Prevent multiple-enumeration. var combinationList = combination as ICollection ?? combination.ToArray(); - return CheckCompatibleSet(combinationList) && CheckAllowed(combinationList, allowedTypes); + return CheckCompatibleSet(combinationList, out _) && CheckAllowed(combinationList, allowedTypes); } /// @@ -38,32 +37,31 @@ namespace osu.Game.Utils /// The combination to check. /// Whether all s in the combination are compatible with each-other. public static bool CheckCompatibleSet(IEnumerable combination) + => CheckCompatibleSet(combination, out _); + + /// + /// Checks that all s in a combination are compatible with each-other. + /// + /// The combination to check. + /// Any invalid mods in the set. + /// Whether all s in the combination are compatible with each-other. + public static bool CheckCompatibleSet(IEnumerable combination, out List? invalidMods) { - var incompatibleTypes = new HashSet(); - var incomingTypes = new HashSet(); + invalidMods = null; - foreach (var mod in combination.SelectMany(FlattenMod)) + foreach (var mod in combination) { - // Add the new mod incompatibilities, checking whether any match the existing mod types. - foreach (var t in mod.IncompatibleMods) + foreach (var type in mod.IncompatibleMods) { - if (incomingTypes.Contains(t)) - return false; - - incompatibleTypes.Add(t); - } - - // Add the new mod types, checking whether any match the incompatible types. - foreach (var t in mod.GetType().EnumerateBaseTypes()) - { - if (incompatibleTypes.Contains(t)) - return false; - - incomingTypes.Add(t); + foreach (var invalid in combination.Where(m => type.IsInstanceOfType(m))) + { + invalidMods ??= new List(); + invalidMods.Add(invalid); + } } } - return true; + return invalidMods == null; } /// From 1df412a03cde9c1ef9363fc1759f8942ea73ec94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:31:08 +0900 Subject: [PATCH 318/385] Fix incorrect handling of multi-mod incompatibilities --- osu.Game.Tests/Mods/ModUtilsTest.cs | 25 ++++++++++++++++++++++++- osu.Game/Utils/ModUtils.cs | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index b602c082bf..7d3dea7ed5 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Mods } [Test] - public void TestIncompatibleThroughMultiMod() + public void TestMultiModIncompatibleWithTopLevel() { var mod1 = new Mock(); @@ -47,6 +47,21 @@ namespace osu.Game.Tests.Mods Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); } + [Test] + public void TestTopLevelIncompatibleWithMultiMod() + { + // The nested mod. + var mod1 = new Mock(); + var multiMod = new MultiMod(new MultiMod(mod1.Object)); + + var mod2 = new Mock(); + mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(CustomMod1) }); + + // Test both orderings. + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, multiMod }), Is.False); + } + [Test] public void TestCompatibleMods() { @@ -83,5 +98,13 @@ namespace osu.Game.Tests.Mods var mod = new Mock(); Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); } + + public abstract class CustomMod1 : Mod + { + } + + public abstract class CustomMod2 : Mod + { + } } } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index a9271db1b5..41f7b1b45c 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -47,6 +47,7 @@ namespace osu.Game.Utils /// Whether all s in the combination are compatible with each-other. public static bool CheckCompatibleSet(IEnumerable combination, out List? invalidMods) { + combination = FlattenMods(combination); invalidMods = null; foreach (var mod in combination) From 9955e0289869be6a14cb67e762a75e9b49a599b1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:33:12 +0900 Subject: [PATCH 319/385] Make more tests use the custom mod classes For safety purposes... In implementing the previous tests, I found that using mod.Object.GetType() can lead to bad assertions since the same ModProxy class is used for all mocked classes. --- osu.Game.Tests/Mods/ModUtilsTest.cs | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 7d3dea7ed5..e4ded602aa 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -14,37 +14,37 @@ namespace osu.Game.Tests.Mods [Test] public void TestModIsCompatibleByItself() { - var mod = new Mock(); + var mod = new Mock(); Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object })); } [Test] public void TestIncompatibleThroughTopLevel() { - var mod1 = new Mock(); - var mod2 = new Mock(); + var mod1 = new Mock(); + var mod2 = new Mock(); mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() }); // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False); } [Test] public void TestMultiModIncompatibleWithTopLevel() { - var mod1 = new Mock(); + var mod1 = new Mock(); // The nested mod. - var mod2 = new Mock(); + var mod2 = new Mock(); mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() }); var multiMod = new MultiMod(new MultiMod(mod2.Object)); // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { multiMod, mod1.Object }), Is.False); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, multiMod }), Is.False); } [Test] @@ -65,37 +65,37 @@ namespace osu.Game.Tests.Mods [Test] public void TestCompatibleMods() { - var mod1 = new Mock(); - var mod2 = new Mock(); + var mod1 = new Mock(); + var mod2 = new Mock(); // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.True); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.True); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.True); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.True); } [Test] public void TestIncompatibleThroughBaseType() { - var mod1 = new Mock(); - var mod2 = new Mock(); - mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType().BaseType }); + var mod1 = new Mock(); + var mod2 = new Mock(); + mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(Mod) }); // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False); + Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False); } [Test] public void TestAllowedThroughMostDerivedType() { - var mod = new Mock(); + var mod = new Mock(); Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); } [Test] public void TestNotAllowedThroughBaseType() { - var mod = new Mock(); + var mod = new Mock(); Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); } From 12f52316cd2f198c0d649f41b46ef2975cfbb16d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:37:11 +0900 Subject: [PATCH 320/385] Prevent multiple enumeration --- osu.Game/Utils/ModUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 41f7b1b45c..9336add465 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -47,7 +47,7 @@ namespace osu.Game.Utils /// Whether all s in the combination are compatible with each-other. public static bool CheckCompatibleSet(IEnumerable combination, out List? invalidMods) { - combination = FlattenMods(combination); + combination = FlattenMods(combination).ToArray(); invalidMods = null; foreach (var mod in combination) From 052cf1abaedec23cf44022aedc3324dacd5a8168 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:42:02 +0900 Subject: [PATCH 321/385] Reuse existing method --- osu.Game/Utils/ModUtils.cs | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 05a07f0459..0eb30cbe36 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -88,40 +88,22 @@ namespace osu.Game.Utils /// The mods to check. /// Invalid mods, if any were found. Can be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. - public static bool CheckValidForGameplay(IEnumerable mods, out Mod[]? invalidMods) + public static bool CheckValidForGameplay(IEnumerable mods, out List? invalidMods) { mods = mods.ToArray(); - List? foundInvalid = null; - - void addInvalid(Mod mod) - { - foundInvalid ??= new List(); - foundInvalid.Add(mod); - } + CheckCompatibleSet(mods, out invalidMods); foreach (var mod in mods) { - bool valid = mod.Type != ModType.System - && mod.HasImplementation - && !(mod is MultiMod); - - if (!valid) + if (mod.Type == ModType.System || !mod.HasImplementation || mod is MultiMod) { - // if this mod was found as invalid, we can exclude it before potentially excluding more incompatible types. - addInvalid(mod); - continue; - } - - foreach (var type in mod.IncompatibleMods) - { - foreach (var invalid in mods.Where(m => type.IsInstanceOfType(m))) - addInvalid(invalid); + invalidMods ??= new List(); + invalidMods.Add(mod); } } - invalidMods = foundInvalid?.ToArray(); - return foundInvalid == null; + return invalidMods == null; } /// From 0a9861d0abe19879a3d9772c248df7ed2b0c2e74 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:51:13 +0900 Subject: [PATCH 322/385] Use TestCaseSource and add multi-mod test --- osu.Game.Tests/Mods/ModUtilsTest.cs | 47 +++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index ff1af88bac..fbdb1e2f3d 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -103,20 +103,43 @@ namespace osu.Game.Tests.Mods Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); } - // test incompatible pair. - [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) }, new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) })] - // test incompatible pair with derived class. - [TestCase(new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) }, new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) })] - // test system mod. - [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModTouchDevice) }, new[] { typeof(OsuModTouchDevice) })] - // test valid. - [TestCase(new[] { typeof(OsuModDoubleTime), typeof(OsuModHardRock) }, null)] - public void TestInvalidModScenarios(Type[] input, Type[] expectedInvalid) + private static readonly object[] invalid_mod_test_scenarios = { - List inputMods = new List(); - foreach (var t in input) - inputMods.Add((Mod)Activator.CreateInstance(t)); + // incompatible pair. + new object[] + { + new Mod[] { new OsuModDoubleTime(), new OsuModHalfTime() }, + new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) } + }, + // incompatible pair with derived class. + new object[] + { + new Mod[] { new OsuModNightcore(), new OsuModHalfTime() }, + new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) } + }, + // system mod. + new object[] + { + new Mod[] { new OsuModDoubleTime(), new OsuModTouchDevice() }, + new[] { typeof(OsuModTouchDevice) } + }, + // multi mod. + new object[] + { + new Mod[] { new MultiMod(new OsuModHalfTime()), new OsuModHalfTime() }, + new[] { typeof(MultiMod) } + }, + // valid pair. + new object[] + { + new Mod[] { new OsuModDoubleTime(), new OsuModHardRock() }, + null + } + }; + [TestCaseSource(nameof(invalid_mod_test_scenarios))] + public void TestInvalidModScenarios(Mod[] inputMods, Type[] expectedInvalid) + { bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid); Assert.That(isValid, Is.EqualTo(expectedInvalid == null)); From 180af3c7f8311d0a4477ecb79e7f7403da9dc85a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 19:02:09 +0900 Subject: [PATCH 323/385] Add codeanalysis attribute --- osu.Game/Utils/ModUtils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 9336add465..8ac5bde65a 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using osu.Game.Rulesets.Mods; @@ -45,7 +46,7 @@ namespace osu.Game.Utils /// The combination to check. /// Any invalid mods in the set. /// Whether all s in the combination are compatible with each-other. - public static bool CheckCompatibleSet(IEnumerable combination, out List? invalidMods) + public static bool CheckCompatibleSet(IEnumerable combination, [NotNullWhen(false)] out List? invalidMods) { combination = FlattenMods(combination).ToArray(); invalidMods = null; From a2e3b1c0e454e81c6836b44ca6f29426040b6ac1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 18:56:57 +0900 Subject: [PATCH 324/385] Move Mods reset code to OnlinePlaySongSelect --- .../Multiplayer/MultiplayerMatchSongSelect.cs | 9 --------- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 11 ++++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index f7f0402555..84e8849726 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; @@ -27,13 +25,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer AddInternal(loadingLayer = new LoadingLayer(true)); } - protected override void LoadComplete() - { - base.LoadComplete(); - - Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); - } - protected override void SelectItem(PlaylistItem item) { // If the client is already in a room, update via the client. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index c58632c500..1c345b883f 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -58,8 +58,17 @@ namespace osu.Game.Screens.OnlinePlay initialRuleset = Ruleset.Value; initialMods = Mods.Value.ToList(); - freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); FooterPanels.Add(freeModSelectOverlay); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods. + // Similarly, freeMods is currently empty but should only contain the allowed mods. + Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); + freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty(); Ruleset.BindValueChanged(onRulesetChanged); } From 41593ff09e4ef0ac405cbd0fdcdba0beff9a29b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 19:14:44 +0900 Subject: [PATCH 325/385] Privatise protected property setters --- osu.Game/Graphics/UserInterface/GrayButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/GrayButton.cs b/osu.Game/Graphics/UserInterface/GrayButton.cs index dd05701545..88c46f29e0 100644 --- a/osu.Game/Graphics/UserInterface/GrayButton.cs +++ b/osu.Game/Graphics/UserInterface/GrayButton.cs @@ -11,8 +11,8 @@ namespace osu.Game.Graphics.UserInterface { public class GrayButton : OsuAnimatedButton { - protected SpriteIcon Icon; - protected Box Background; + protected SpriteIcon Icon { get; private set; } + protected Box Background { get; private set; } private readonly IconUsage icon; From 8e70a50af0836af6fb821c69a7239c7cad9e1eec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Feb 2021 19:22:13 +0900 Subject: [PATCH 326/385] Remove unused using statement --- osu.Game.Tests/Mods/ModUtilsTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index fbdb1e2f3d..7dcaabca3d 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using Moq; using NUnit.Framework; From 2dece12a7c6041fb8ff9e5a4896c77b78865de3a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 19:57:42 +0900 Subject: [PATCH 327/385] Disable/disallow freemods on incompatible/selected mods --- osu.Game/Overlays/Mods/ModSection.cs | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 993f4ef9d7..728c726b82 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -130,13 +130,13 @@ namespace osu.Game.Overlays.Mods /// Updates all buttons with the given list of selected mods. /// /// The new list of selected mods to select. - public void UpdateSelectedMods(IReadOnlyList newSelectedMods) + public void UpdateSelectedButtons(IReadOnlyList newSelectedMods) { foreach (var button in buttons) - updateButtonMods(button, newSelectedMods); + updateButtonSelection(button, newSelectedMods); } - private void updateButtonMods(ModButton button, IReadOnlyList newSelectedMods) + private void updateButtonSelection(ModButton button, IReadOnlyList newSelectedMods) { foreach (var mod in newSelectedMods) { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b20c2d9d19..56d6008b00 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -377,7 +377,7 @@ namespace osu.Game.Overlays.Mods base.LoadComplete(); availableMods.BindValueChanged(_ => updateAvailableMods(), true); - SelectedMods.BindValueChanged(selectedModsChanged, true); + SelectedMods.BindValueChanged(_ => updateSelectedButtons(), true); } protected override void PopOut() @@ -445,6 +445,8 @@ namespace osu.Game.Overlays.Mods section.Mods = modEnumeration.Select(validModOrNull).Where(m => m != null); } + + updateSelectedButtons(); } /// @@ -465,10 +467,13 @@ namespace osu.Game.Overlays.Mods return validSubset.Length == 0 ? null : new MultiMod(validSubset); } - private void selectedModsChanged(ValueChangedEvent> mods) + private void updateSelectedButtons() { + // Enumeration below may update the bindable list. + var selectedMods = SelectedMods.Value.ToList(); + foreach (var section in ModSectionsContainer.Children) - section.UpdateSelectedMods(mods.NewValue); + section.UpdateSelectedButtons(selectedMods); updateMods(); } From 3741f05ab335d5baeb755bd4e370310698c1fe7f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:11:40 +0900 Subject: [PATCH 328/385] Refactor mod sections and make them overridable --- osu.Game/Overlays/Mods/ModSection.cs | 39 +++++++++---------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 38 ++++++++++++++---- .../Mods/Sections/AutomationSection.cs | 19 --------- .../Mods/Sections/ConversionSection.cs | 19 --------- .../Sections/DifficultyIncreaseSection.cs | 19 --------- .../Sections/DifficultyReductionSection.cs | 19 --------- osu.Game/Overlays/Mods/Sections/FunSection.cs | 19 --------- 7 files changed, 50 insertions(+), 122 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/Sections/AutomationSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/ConversionSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs delete mode 100644 osu.Game/Overlays/Mods/Sections/FunSection.cs diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 573d1e5355..89a3e2f5cd 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -11,26 +11,23 @@ using System; using System.Linq; using System.Collections.Generic; using System.Threading; +using Humanizer; using osu.Framework.Input.Events; using osu.Game.Graphics; namespace osu.Game.Overlays.Mods { - public abstract class ModSection : Container + public class ModSection : CompositeDrawable { - private readonly OsuSpriteText headerLabel; + private readonly Drawable header; public FillFlowContainer ButtonsContainer { get; } public Action Action; - protected abstract Key[] ToggleKeys { get; } - public abstract ModType ModType { get; } - public string Header - { - get => headerLabel.Text; - set => headerLabel.Text = value; - } + public Key[] ToggleKeys; + + public readonly ModType ModType; public IEnumerable SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null); @@ -61,7 +58,7 @@ namespace osu.Game.Overlays.Mods if (modContainers.Length == 0) { ModIconsLoaded = true; - headerLabel.Hide(); + header.Hide(); Hide(); return; } @@ -76,7 +73,7 @@ namespace osu.Game.Overlays.Mods buttons = modContainers.OfType().ToArray(); - headerLabel.FadeIn(200); + header.FadeIn(200); this.FadeIn(200); } } @@ -153,23 +150,19 @@ namespace osu.Game.Overlays.Mods button.Deselect(); } - protected ModSection() + public ModSection(ModType type) { + ModType = type; + AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; Origin = Anchor.TopCentre; Anchor = Anchor.TopCentre; - Children = new Drawable[] + InternalChildren = new[] { - headerLabel = new OsuSpriteText - { - Origin = Anchor.TopLeft, - Anchor = Anchor.TopLeft, - Position = new Vector2(0f, 0f), - Font = OsuFont.GetFont(weight: FontWeight.Bold) - }, + header = CreateHeader(type.Humanize(LetterCasing.Title)), ButtonsContainer = new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -185,5 +178,11 @@ namespace osu.Game.Overlays.Mods }, }; } + + protected virtual Drawable CreateHeader(string text) => new OsuSpriteText + { + Font = OsuFont.GetFont(weight: FontWeight.Bold), + Text = text + }; } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 1258ba719d..fd6f771f16 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -19,7 +19,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osuTK; @@ -190,13 +189,31 @@ namespace osu.Game.Overlays.Mods Width = content_width, LayoutDuration = 200, LayoutEasing = Easing.OutQuint, - Children = new ModSection[] + Children = new[] { - new DifficultyReductionSection { Action = modButtonPressed }, - new DifficultyIncreaseSection { Action = modButtonPressed }, - new AutomationSection { Action = modButtonPressed }, - new ConversionSection { Action = modButtonPressed }, - new FunSection { Action = modButtonPressed }, + CreateModSection(ModType.DifficultyReduction).With(s => + { + s.ToggleKeys = new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.DifficultyIncrease).With(s => + { + s.ToggleKeys = new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Automation).With(s => + { + s.ToggleKeys = new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Conversion).With(s => + { + s.Action = modButtonPressed; + }), + CreateModSection(ModType.Fun).With(s => + { + s.Action = modButtonPressed; + }), } }, } @@ -454,6 +471,13 @@ namespace osu.Game.Overlays.Mods private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); + /// + /// Creates a that groups s with the same . + /// + /// The of s in the section. + /// The . + protected virtual ModSection CreateModSection(ModType type) => new ModSection(type); + #region Disposal protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Overlays/Mods/Sections/AutomationSection.cs b/osu.Game/Overlays/Mods/Sections/AutomationSection.cs deleted file mode 100644 index a2d7fec15f..0000000000 --- a/osu.Game/Overlays/Mods/Sections/AutomationSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class AutomationSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; - public override ModType ModType => ModType.Automation; - - public AutomationSection() - { - Header = @"Automation"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/ConversionSection.cs b/osu.Game/Overlays/Mods/Sections/ConversionSection.cs deleted file mode 100644 index 24fd8c30dd..0000000000 --- a/osu.Game/Overlays/Mods/Sections/ConversionSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class ConversionSection : ModSection - { - protected override Key[] ToggleKeys => null; - public override ModType ModType => ModType.Conversion; - - public ConversionSection() - { - Header = @"Conversion"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs deleted file mode 100644 index 0b7ccd1f25..0000000000 --- a/osu.Game/Overlays/Mods/Sections/DifficultyIncreaseSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class DifficultyIncreaseSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; - public override ModType ModType => ModType.DifficultyIncrease; - - public DifficultyIncreaseSection() - { - Header = @"Difficulty Increase"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs deleted file mode 100644 index 508e92508b..0000000000 --- a/osu.Game/Overlays/Mods/Sections/DifficultyReductionSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class DifficultyReductionSection : ModSection - { - protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; - public override ModType ModType => ModType.DifficultyReduction; - - public DifficultyReductionSection() - { - Header = @"Difficulty Reduction"; - } - } -} diff --git a/osu.Game/Overlays/Mods/Sections/FunSection.cs b/osu.Game/Overlays/Mods/Sections/FunSection.cs deleted file mode 100644 index af1f5836b1..0000000000 --- a/osu.Game/Overlays/Mods/Sections/FunSection.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods.Sections -{ - public class FunSection : ModSection - { - protected override Key[] ToggleKeys => null; - public override ModType ModType => ModType.Fun; - - public FunSection() - { - Header = @"Fun"; - } - } -} From 6d620264f48369f807a79983da19bf2ff37773e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:27:41 +0900 Subject: [PATCH 329/385] Allow mod buttons to not be stacked --- .../TestSceneModSelectOverlay.cs | 62 ++++++++++++------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 12 +++- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index bd4010a7f3..71c549b433 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -38,28 +38,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [SetUp] - public void SetUp() => Schedule(() => - { - SelectedMods.Value = Array.Empty(); - Children = new Drawable[] - { - modSelect = new TestModSelectOverlay - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - SelectedMods = { BindTarget = SelectedMods } - }, - - modDisplay = new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Position = new Vector2(-5, 25), - Current = { BindTarget = modSelect.SelectedMods } - } - }; - }); + public void SetUp() => Schedule(() => createDisplay(() => new TestModSelectOverlay())); [SetUpSteps] public void SetUpSteps() @@ -146,6 +125,18 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestNonStacked() + { + changeRuleset(0); + + AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay())); + + AddStep("show", () => modSelect.Show()); + + AddAssert("ensure all buttons are spread out", () => modSelect.ChildrenOfType().All(m => m.Mods.Length <= 1)); + } + private void testSingleMod(Mod mod) { selectNext(mod); @@ -265,6 +256,28 @@ namespace osu.Game.Tests.Visual.UserInterface private void checkLabelColor(Func getColour) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == getColour()); + private void createDisplay(Func createOverlayFunc) + { + SelectedMods.Value = Array.Empty(); + Children = new Drawable[] + { + modSelect = createOverlayFunc().With(d => + { + d.Origin = Anchor.BottomCentre; + d.Anchor = Anchor.BottomCentre; + d.SelectedMods.BindTarget = SelectedMods; + }), + modDisplay = new ModDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Position = new Vector2(-5, 25), + Current = { BindTarget = modSelect.SelectedMods } + } + }; + } + private class TestModSelectOverlay : ModSelectOverlay { public new Bindable> SelectedMods => base.SelectedMods; @@ -283,5 +296,10 @@ namespace osu.Game.Tests.Visual.UserInterface public new Color4 LowMultiplierColour => base.LowMultiplierColour; public new Color4 HighMultiplierColour => base.HighMultiplierColour; } + + private class TestNonStackedModSelectOverlay : TestModSelectOverlay + { + protected override bool Stacked => false; + } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 1258ba719d..c7e856028a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -22,6 +22,7 @@ using osu.Game.Input.Bindings; using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets.Mods; using osu.Game.Screens; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -43,6 +44,11 @@ namespace osu.Game.Overlays.Mods protected override bool DimMainContent => false; + /// + /// Whether s underneath the same instance should appear as stacked buttons. + /// + protected virtual bool Stacked => true; + protected readonly FillFlowContainer ModSectionsContainer; protected readonly ModSettingsContainer ModSettingsContainer; @@ -405,7 +411,11 @@ namespace osu.Game.Overlays.Mods if (mods.NewValue == null) return; foreach (var section in ModSectionsContainer.Children) - section.Mods = mods.NewValue[section.ModType].Where(isValidMod); + { + section.Mods = Stacked + ? availableMods.Value[section.ModType] + : ModUtils.FlattenMods(availableMods.Value[section.ModType]); + } } private void selectedModsChanged(ValueChangedEvent> mods) From 75f81bfa062c9199759cd8257a10e64e543eb8ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:35:31 +0900 Subject: [PATCH 330/385] Add back mod validation --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c7e856028a..775b0de1c0 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -412,9 +412,12 @@ namespace osu.Game.Overlays.Mods foreach (var section in ModSectionsContainer.Children) { - section.Mods = Stacked - ? availableMods.Value[section.ModType] - : ModUtils.FlattenMods(availableMods.Value[section.ModType]); + IEnumerable modEnumeration = availableMods.Value[section.ModType]; + + if (!Stacked) + modEnumeration = ModUtils.FlattenMods(modEnumeration); + + section.Mods = modEnumeration.Where(isValidMod); } } From 10ceddf3ffcf861f71aee5f4a681441b15913226 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:47:50 +0900 Subject: [PATCH 331/385] Make IsValidMod adjustable --- .../TestSceneModSelectOverlay.cs | 16 ++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 54 ++++++++++++++++--- .../Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 71c549b433..9cf8b95ddf 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -137,6 +137,22 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("ensure all buttons are spread out", () => modSelect.ChildrenOfType().All(m => m.Mods.Length <= 1)); } + [Test] + public void TestChangeIsValidChangesButtonVisibility() + { + changeRuleset(0); + + AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); + + AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime)); + AddAssert("double time not visible", () => modSelect.ChildrenOfType().All(b => !b.Mods.Any(m => m is OsuModDoubleTime))); + AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); + + AddStep("make double time valid again", () => modSelect.IsValidMod = m => true); + AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); + AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); + } + private void testSingleMod(Mod mod) { selectNext(mod); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 775b0de1c0..61e4b45495 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -31,7 +32,6 @@ namespace osu.Game.Overlays.Mods { public class ModSelectOverlay : WaveOverlayContainer { - private readonly Func isValidMod; public const float HEIGHT = 510; protected readonly TriangleButton DeselectAllButton; @@ -49,6 +49,23 @@ namespace osu.Game.Overlays.Mods /// protected virtual bool Stacked => true; + [NotNull] + private Func isValidMod = m => true; + + /// + /// A function that checks whether a given mod is selectable. + /// + [NotNull] + public Func IsValidMod + { + get => isValidMod; + set + { + isValidMod = value ?? throw new ArgumentNullException(nameof(value)); + updateAvailableMods(); + } + } + protected readonly FillFlowContainer ModSectionsContainer; protected readonly ModSettingsContainer ModSettingsContainer; @@ -67,10 +84,8 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - public ModSelectOverlay(Func isValidMod = null) + public ModSelectOverlay() { - this.isValidMod = isValidMod ?? (m => true); - Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); @@ -351,7 +366,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - availableMods.BindValueChanged(availableModsChanged, true); + availableMods.BindValueChanged(_ => updateAvailableMods(), true); SelectedMods.BindValueChanged(selectedModsChanged, true); } @@ -406,9 +421,10 @@ namespace osu.Game.Overlays.Mods public override bool OnPressed(GlobalAction action) => false; // handled by back button - private void availableModsChanged(ValueChangedEvent>> mods) + private void updateAvailableMods() { - if (mods.NewValue == null) return; + if (availableMods?.Value == null) + return; foreach (var section in ModSectionsContainer.Children) { @@ -417,10 +433,32 @@ namespace osu.Game.Overlays.Mods if (!Stacked) modEnumeration = ModUtils.FlattenMods(modEnumeration); - section.Mods = modEnumeration.Where(isValidMod); + section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null); } } + /// + /// Returns a valid form of a given if possible, or null otherwise. + /// + /// + /// This is a recursive process during which any invalid mods are culled while preserving structures where possible. + /// + /// The to check. + /// A valid form of if exists, or null otherwise. + [CanBeNull] + private Mod getValidModOrNull([NotNull] Mod mod) + { + if (!(mod is MultiMod multi)) + return IsValidMod(mod) ? mod : null; + + var validSubset = multi.Mods.Select(getValidModOrNull).Where(m => m != null).ToArray(); + + if (validSubset.Length == 0) + return null; + + return validSubset.Length == 1 ? validSubset[0] : new MultiMod(validSubset); + } + private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index ebc06d2445..5bf9b1ee7e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay { IsValidMod = isValidMod }; private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index ed47b5d5ac..1bb7374ce3 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Select item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(isValidMod); + protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay { IsValidMod = isValidMod }; private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } From 50e92bd0ed729a2d4aa551bea13a66f37de6035c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:50:54 +0900 Subject: [PATCH 332/385] Fix selection not being preserved when IsValidMod changes --- .../UserInterface/TestSceneModSelectOverlay.cs | 12 ++++++++++++ osu.Game/Overlays/Mods/ModSection.cs | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 11 ++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 9cf8b95ddf..81edcd8db8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -153,6 +153,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); } + [Test] + public void TestChangeIsValidPreservesSelection() + { + changeRuleset(0); + + AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); + AddAssert("DT + HD selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); + + AddStep("make NF invalid", () => modSelect.IsValidMod = m => !(m is ModNoFail)); + AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); + } + private void testSingleMod(Mod mod) { selectNext(mod); diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 573d1e5355..4c629aef54 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -130,13 +130,13 @@ namespace osu.Game.Overlays.Mods /// Updates all buttons with the given list of selected mods. /// /// The new list of selected mods to select. - public void UpdateSelectedMods(IReadOnlyList newSelectedMods) + public void UpdateSelectedButtons(IReadOnlyList newSelectedMods) { foreach (var button in buttons) - updateButtonMods(button, newSelectedMods); + updateButtonSelection(button, newSelectedMods); } - private void updateButtonMods(ModButton button, IReadOnlyList newSelectedMods) + private void updateButtonSelection(ModButton button, IReadOnlyList newSelectedMods) { foreach (var mod in newSelectedMods) { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 61e4b45495..fcec6f3926 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -367,7 +367,7 @@ namespace osu.Game.Overlays.Mods base.LoadComplete(); availableMods.BindValueChanged(_ => updateAvailableMods(), true); - SelectedMods.BindValueChanged(selectedModsChanged, true); + SelectedMods.BindValueChanged(_ => updateSelectedButtons(), true); } protected override void PopOut() @@ -435,6 +435,8 @@ namespace osu.Game.Overlays.Mods section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null); } + + updateSelectedButtons(); } /// @@ -459,10 +461,13 @@ namespace osu.Game.Overlays.Mods return validSubset.Length == 1 ? validSubset[0] : new MultiMod(validSubset); } - private void selectedModsChanged(ValueChangedEvent> mods) + private void updateSelectedButtons() { + // Enumeration below may update the bindable list. + var selectedMods = SelectedMods.Value.ToList(); + foreach (var section in ModSectionsContainer.Children) - section.UpdateSelectedMods(mods.NewValue); + section.UpdateSelectedButtons(selectedMods); updateMods(); } From e58ece9e108b757a72aaff635090ad00b245aa5a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 20:58:31 +0900 Subject: [PATCH 333/385] Make ModSelectOverlay abstract --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- .../Visual/UserInterface/TestSceneModSettings.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/Mods/SoloModSelectOverlay.cs | 9 +++++++++ .../OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 7 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Overlays/Mods/SoloModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 81edcd8db8..92104cfc72 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -306,7 +306,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } - private class TestModSelectOverlay : ModSelectOverlay + private class TestModSelectOverlay : SoloModSelectOverlay { public new Bindable> SelectedMods => base.SelectedMods; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 8614700b15..3c889bdec4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded); } - private class TestModSelectOverlay : ModSelectOverlay + private class TestModSelectOverlay : SoloModSelectOverlay { public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer; public new TriangleButton CustomiseButton => base.CustomiseButton; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index fcec6f3926..75ad90f065 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -30,7 +30,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class ModSelectOverlay : WaveOverlayContainer + public abstract class ModSelectOverlay : WaveOverlayContainer { public const float HEIGHT = 510; @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Mods private SampleChannel sampleOn, sampleOff; - public ModSelectOverlay() + protected ModSelectOverlay() { Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs new file mode 100644 index 0000000000..8f6819d7ff --- /dev/null +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -0,0 +1,9 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Overlays.Mods +{ + public class SoloModSelectOverlay : ModSelectOverlay + { + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 5bf9b1ee7e..930f70d087 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea(); - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay { IsValidMod = isValidMod }; + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay { IsValidMod = isValidMod }; private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 1bb7374ce3..e181370cf7 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Select item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy())); } - protected override ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay { IsValidMod = isValidMod }; + protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay { IsValidMod = isValidMod }; private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4fca77a176..ff49dd9f7e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -301,7 +301,7 @@ namespace osu.Game.Screens.Select } } - protected virtual ModSelectOverlay CreateModSelectOverlay() => new ModSelectOverlay(); + protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 728f8599b2bedbc721481c2bafb017a130177d25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:06:32 +0900 Subject: [PATCH 334/385] Move incompatible mod deselection to SoloModOverlay --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 23 ++++++++----------- .../Overlays/Mods/SoloModSelectOverlay.cs | 9 ++++++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 75ad90f065..c400e4cc43 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -349,19 +349,6 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - /// - /// Deselect one or more mods. - /// - /// The types of s which should be deselected. - /// Set to true to bypass animations and update selections immediately. - private void deselectTypes(Type[] modTypes, bool immediate = false) - { - if (modTypes.Length == 0) return; - - foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(modTypes, immediate); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -496,7 +483,7 @@ namespace osu.Game.Overlays.Mods { if (State.Value == Visibility.Visible) sampleOn?.Play(); - deselectTypes(selectedMod.IncompatibleMods, true); + OnModSelected(selectedMod); if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show(); } @@ -508,6 +495,14 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } + /// + /// Invoked when a new has been selected. + /// + /// The that has been selected. + protected virtual void OnModSelected(Mod mod) + { + } + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); #region Disposal diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs index 8f6819d7ff..d039ad1f98 100644 --- a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -1,9 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Mods; + namespace osu.Game.Overlays.Mods { public class SoloModSelectOverlay : ModSelectOverlay { + protected override void OnModSelected(Mod mod) + { + base.OnModSelected(mod); + + foreach (var section in ModSectionsContainer.Children) + section.DeselectTypes(mod.IncompatibleMods, true); + } } } From 643c0605d85cf7808394c4b2fe3ea4f39b62906b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:14:38 +0900 Subject: [PATCH 335/385] Implement the freemod selection overlay --- .../TestSceneFreeModSelectOverlay.cs | 21 +++ osu.Game/Overlays/Mods/ModSection.cs | 15 ++- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 19 +-- .../OnlinePlay/FreeModSelectOverlay.cs | 120 ++++++++++++++++++ 4 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs create mode 100644 osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs new file mode 100644 index 0000000000..26a0301d8a --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneFreeModSelectOverlay : MultiplayerTestScene + { + [SetUp] + public new void Setup() => Schedule(() => + { + Child = new FreeModSelectOverlay + { + State = { Value = Visibility.Visible } + }; + }); + } +} diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index b3ddd30772..87a45ebf63 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -94,7 +94,20 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } - public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); + /// + /// Selects all mods. + /// + public void SelectAll() + { + foreach (var button in buttons.Where(b => !b.Selected)) + button.SelectAt(0); + } + + /// + /// Deselects all mods. + /// + /// Set to true to bypass animations and update selections immediately. + public void DeselectAll(bool immediate = false) => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null), immediate); /// /// Deselect one or more mods in this section. diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e064a6fb84..8225c1b6bb 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -37,8 +37,11 @@ namespace osu.Game.Overlays.Mods protected readonly TriangleButton CustomiseButton; protected readonly TriangleButton CloseButton; + protected readonly Drawable MultiplierSection; protected readonly OsuSpriteText MultiplierLabel; + protected readonly FillFlowContainer FooterContainer; + protected override bool BlockNonPositionalInput => false; protected override bool DimMainContent => false; @@ -79,8 +82,6 @@ namespace osu.Game.Overlays.Mods private const float content_width = 0.8f; private const float footer_button_spacing = 20; - private readonly FillFlowContainer footerContainer; - private SampleChannel sampleOn, sampleOff; protected ModSelectOverlay() @@ -269,7 +270,7 @@ namespace osu.Game.Overlays.Mods Colour = new Color4(172, 20, 116, 255), Alpha = 0.5f, }, - footerContainer = new FillFlowContainer + FooterContainer = new FillFlowContainer { Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, @@ -283,7 +284,7 @@ namespace osu.Game.Overlays.Mods Vertical = 15, Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] + Children = new[] { DeselectAllButton = new TriangleButton { @@ -310,7 +311,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, }, - new FillFlowContainer + MultiplierSection = new FillFlowContainer { AutoSizeAxes = Axes.Both, Spacing = new Vector2(footer_button_spacing / 2, 0), @@ -378,8 +379,8 @@ namespace osu.Game.Overlays.Mods { base.PopOut(); - footerContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); + FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); foreach (var section in ModSectionsContainer.Children) { @@ -393,8 +394,8 @@ namespace osu.Game.Overlays.Mods { base.PopIn(); - footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); + FooterContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); + FooterContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); foreach (var section in ModSectionsContainer.Children) { diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs new file mode 100644 index 0000000000..628199309a --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -0,0 +1,120 @@ +// 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Screens.OnlinePlay +{ + /// + /// A used for free-mod selection in online play. + /// + public class FreeModSelectOverlay : ModSelectOverlay + { + protected override bool Stacked => false; + + public FreeModSelectOverlay() + { + CustomiseButton.Alpha = 0; + MultiplierSection.Alpha = 0; + DeselectAllButton.Alpha = 0; + + Drawable selectAllButton; + Drawable deselectAllButton; + + FooterContainer.AddRange(new[] + { + selectAllButton = new TriangleButton + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Width = 180, + Text = "Select All", + Action = selectAll, + }, + // Unlike the base mod select overlay, this button deselects mods instantaneously. + deselectAllButton = new TriangleButton + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Width = 180, + Text = "Deselect All", + Action = deselectAll, + }, + }); + + FooterContainer.SetLayoutPosition(selectAllButton, -2); + FooterContainer.SetLayoutPosition(deselectAllButton, -1); + } + + private void selectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.SelectAll(); + } + + private void deselectAll() + { + foreach (var section in ModSectionsContainer.Children) + section.DeselectAll(true); + } + + protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); + + private class FreeModSection : ModSection + { + private HeaderCheckbox checkbox; + + public FreeModSection(ModType type) + : base(type) + { + } + + protected override Drawable CreateHeader(string text) => new Container + { + AutoSizeAxes = Axes.Y, + Width = 175, + Child = checkbox = new HeaderCheckbox + { + LabelText = text, + Changed = onCheckboxChanged + } + }; + + private void onCheckboxChanged(bool value) + { + foreach (var button in ButtonsContainer.OfType()) + { + if (value) + button.SelectAt(0); + else + button.Deselect(); + } + } + + protected override void Update() + { + base.Update(); + + var validButtons = ButtonsContainer.OfType().Where(b => b.Mod.HasImplementation); + checkbox.Current.Value = validButtons.All(b => b.Selected); + } + } + + private class HeaderCheckbox : OsuCheckbox + { + public Action Changed; + + protected override void OnUserChange(bool value) + { + base.OnUserChange(value); + Changed?.Invoke(value); + } + } + } +} From f25535548ae1910215fff01186cac34285cdac81 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:20:16 +0900 Subject: [PATCH 336/385] Fix buzzing on select all/deselect all --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8225c1b6bb..c308dc2451 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -495,11 +496,17 @@ namespace osu.Game.Overlays.Mods MultiplierLabel.FadeColour(Color4.White, 200); } + private ScheduledDelegate sampleOnDelegate; + private ScheduledDelegate sampleOffDelegate; + private void modButtonPressed(Mod selectedMod) { if (selectedMod != null) { - if (State.Value == Visibility.Visible) sampleOn?.Play(); + // Fixes buzzing when multiple mods are selected in the same frame. + sampleOnDelegate?.Cancel(); + if (State.Value == Visibility.Visible) + sampleOnDelegate = Scheduler.Add(() => sampleOn?.Play()); OnModSelected(selectedMod); @@ -507,7 +514,10 @@ namespace osu.Game.Overlays.Mods } else { - if (State.Value == Visibility.Visible) sampleOff?.Play(); + // Fixes buzzing when multiple mods are deselected in the same frame. + sampleOffDelegate?.Cancel(); + if (State.Value == Visibility.Visible) + sampleOffDelegate = Scheduler.Add(() => sampleOff?.Play()); } refreshSelectedMods(); From 5a56e2ba4b66ae8058157a3b32935f154d0f0c02 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:29:00 +0900 Subject: [PATCH 337/385] Fix sound duplication due to checkbox --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 17 +++++++++++++---- .../Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 6593531099..517f83daa9 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -18,6 +18,11 @@ namespace osu.Game.Graphics.UserInterface public Color4 UncheckedColor { get; set; } = Color4.White; public int FadeDuration { get; set; } + /// + /// Whether to play sounds when the state changes as a result of user interaction. + /// + protected virtual bool PlaySoundsOnUserChange => true; + public string LabelText { set @@ -96,10 +101,14 @@ namespace osu.Game.Graphics.UserInterface protected override void OnUserChange(bool value) { base.OnUserChange(value); - if (value) - sampleChecked?.Play(); - else - sampleUnchecked?.Play(); + + if (PlaySoundsOnUserChange) + { + if (value) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); + } } } } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 628199309a..608e58b534 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -110,6 +110,8 @@ namespace osu.Game.Screens.OnlinePlay { public Action Changed; + protected override bool PlaySoundsOnUserChange => false; + protected override void OnUserChange(bool value) { base.OnUserChange(value); From 6ff8e8dd37c3a79ea7b6c122f596272708cd6e58 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:29:08 +0900 Subject: [PATCH 338/385] Disable a few mods by default --- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 608e58b534..5b9a19897f 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -18,8 +18,16 @@ namespace osu.Game.Screens.OnlinePlay { protected override bool Stacked => false; + public new Func IsValidMod + { + get => base.IsValidMod; + set => base.IsValidMod = m => m.HasImplementation && !m.RequiresConfiguration && !(m is ModAutoplay) && value(m); + } + public FreeModSelectOverlay() { + IsValidMod = m => true; + CustomiseButton.Alpha = 0; MultiplierSection.Alpha = 0; DeselectAllButton.Alpha = 0; From 921f008217719bb6768b54c7746eef2d7d0f0ba7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:35:08 +0900 Subject: [PATCH 339/385] Fix ModIcon not updating background colour correctly --- .../Visual/UserInterface/TestSceneModIcon.cs | 21 +++++++++ osu.Game/Rulesets/UI/ModIcon.cs | 46 +++++++++++-------- 2 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs new file mode 100644 index 0000000000..e7fa7d9235 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModIcon : OsuTestScene + { + [Test] + public void TestChangeModType() + { + ModIcon icon = null; + + AddStep("create mod icon", () => Child = icon = new ModIcon(new OsuModDoubleTime())); + AddStep("change mod", () => icon.Mod = new OsuModEasy()); + } + } +} diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 04a2e052fa..cae5da3d16 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.UI private const float size = 80; - private readonly ModType type; - public virtual string TooltipText => showTooltip ? mod.IconTooltip : null; private Mod mod; @@ -42,10 +40,18 @@ namespace osu.Game.Rulesets.UI set { mod = value; - updateMod(value); + + if (IsLoaded) + updateMod(value); } } + [Resolved] + private OsuColour colours { get; set; } + + private Color4 backgroundColour; + private Color4 highlightedColour; + /// /// Construct a new instance. /// @@ -56,8 +62,6 @@ namespace osu.Game.Rulesets.UI this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); this.showTooltip = showTooltip; - type = mod.Type; - Size = new Vector2(size); Children = new Drawable[] @@ -89,6 +93,13 @@ namespace osu.Game.Rulesets.UI Icon = FontAwesome.Solid.Question }, }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Selected.BindValueChanged(_ => updateColour()); updateMod(mod); } @@ -102,20 +113,14 @@ namespace osu.Game.Rulesets.UI { modIcon.FadeOut(); modAcronym.FadeIn(); - return; + } + else + { + modIcon.FadeIn(); + modAcronym.FadeOut(); } - modIcon.FadeIn(); - modAcronym.FadeOut(); - } - - private Color4 backgroundColour; - private Color4 highlightedColour; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - switch (type) + switch (value.Type) { default: case ModType.DifficultyIncrease: @@ -149,12 +154,13 @@ namespace osu.Game.Rulesets.UI modIcon.Colour = colours.Yellow; break; } + + updateColour(); } - protected override void LoadComplete() + private void updateColour() { - base.LoadComplete(); - Selected.BindValueChanged(selected => background.Colour = selected.NewValue ? highlightedColour : backgroundColour, true); + background.Colour = Selected.Value ? highlightedColour : backgroundColour; } } } From aeb3ed8bb3bb12253eb2b4cf2092634e3c4c62ac Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Feb 2021 21:46:22 +0900 Subject: [PATCH 340/385] Renamespace footer button --- osu.Game/Screens/OnlinePlay/{Match => }/FooterButtonFreeMods.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename osu.Game/Screens/OnlinePlay/{Match => }/FooterButtonFreeMods.cs (97%) diff --git a/osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs similarity index 97% rename from osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs rename to osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs index ca2db877c3..a3cc383b67 100644 --- a/osu.Game/Screens/OnlinePlay/Match/FooterButtonFreeMods.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs @@ -13,7 +13,7 @@ using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Select; using osuTK; -namespace osu.Game.Screens.OnlinePlay.Match +namespace osu.Game.Screens.OnlinePlay { public class FooterButtonFreeMods : FooterButton, IHasCurrentValue> { diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 1c345b883f..0baa663578 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -15,7 +15,6 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.Select; using osu.Game.Utils; From 8e96ffd1e6ea4d848766c72e703503de6b23cffd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Feb 2021 16:54:40 +0300 Subject: [PATCH 341/385] Fix "wait for import" until step potentially finishing early If not obvious, the issue with previous code is that it was checking for `IsAvailableLocally`, while the import is happening on a different thread, so that method could return `true` before the importing has finished and `ItemUpdated` event is called. --- .../TestSceneMultiplayerBeatmapAvailabilityTracker.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs index 3c3793670a..646c4139af 100644 --- a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Online addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing); AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true)); - AddUntilStep("wait for import", () => beatmaps.IsAvailableLocally(testBeatmapSet)); + AddUntilStep("wait for import", () => beatmaps.CurrentImportTask?.IsCompleted == true); addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable); } @@ -127,8 +127,6 @@ namespace osu.Game.Tests.Online private void addAvailabilityCheckStep(string description, Func expected) { - // In DownloadTrackingComposite, state changes are scheduled one frame later, wait one step. - AddWaitStep("wait for potential change", 1); AddAssert(description, () => availablilityTracker.Availability.Value.Equals(expected.Invoke())); } @@ -157,6 +155,8 @@ namespace osu.Game.Tests.Online { public TaskCompletionSource AllowImport = new TaskCompletionSource(); + public Task CurrentImportTask { get; private set; } + protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new TestDownloadRequest(set); @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Online public override async Task Import(BeatmapSetInfo item, ArchiveReader archive = null, CancellationToken cancellationToken = default) { await AllowImport.Task; - return await base.Import(item, archive, cancellationToken); + return await (CurrentImportTask = base.Import(item, archive, cancellationToken)); } } From 50d57a39317c32d59e30006679a030bedfbdb10b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Feb 2021 17:07:16 +0300 Subject: [PATCH 342/385] Move tracker loading into BDL --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index f2cb1120cb..cdf889e4f1 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -41,11 +41,11 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected readonly MultiplayerBeatmapAvailablilityTracker BeatmapAvailablilityTracker; + protected MultiplayerBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } protected RoomSubScreen() { - InternalChild = BeatmapAvailablilityTracker = new MultiplayerBeatmapAvailablilityTracker + BeatmapAvailablilityTracker = new MultiplayerBeatmapAvailablilityTracker { SelectedItem = { BindTarget = SelectedItem }, }; @@ -54,6 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Match [BackgroundDependencyLoader] private void load(AudioManager audio) { + AddInternal(BeatmapAvailablilityTracker); + sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); } From 62d0036c819c0516005f9b5b3938d359e0cac987 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Feb 2021 17:45:07 +0300 Subject: [PATCH 343/385] Fix using private constructor on MessagePack object --- 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 2adeb9b959..a83327aad5 100644 --- a/osu.Game/Online/Rooms/BeatmapAvailability.cs +++ b/osu.Game/Online/Rooms/BeatmapAvailability.cs @@ -26,7 +26,7 @@ namespace osu.Game.Online.Rooms public readonly float? DownloadProgress; [JsonConstructor] - private BeatmapAvailability(DownloadState state, float? downloadProgress = null) + public BeatmapAvailability(DownloadState state, float? downloadProgress = null) { State = state; DownloadProgress = downloadProgress; From 181d2c672b5c93c1dfb84929b78a413dfe326db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Feb 2021 22:05:25 +0100 Subject: [PATCH 344/385] Fix outdated comment --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a7015ba1c4..2d438bd96e 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -343,7 +343,7 @@ namespace osu.Game.Screens.OnlinePlay Colour = Color4.Black, Width = 0.4f, }, - // Piecewise-linear gradient with 3 segments to make it appear smoother + // Piecewise-linear gradient with 2 segments to make it appear smoother new Box { RelativeSizeAxes = Axes.Both, From fc84ec131347ee220c9c9df63561b3e6a099156f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Feb 2021 22:18:14 +0100 Subject: [PATCH 345/385] Move anchor specification to central place --- .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 2d438bd96e..23c713a2c1 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -220,7 +220,11 @@ namespace osu.Game.Screens.OnlinePlay AutoSizeAxes = Axes.Both, Spacing = new Vector2(5), X = -10, - ChildrenEnumerable = CreateButtons() + ChildrenEnumerable = CreateButtons().Select(button => button.With(b => + { + b.Anchor = Anchor.Centre; + b.Origin = Anchor.Centre; + })) } } }; @@ -231,14 +235,10 @@ namespace osu.Game.Screens.OnlinePlay { new PlaylistDownloadButton(Item) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Size = new Vector2(50, 30) }, new PlaylistRemoveButton { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Size = new Vector2(30, 30), Alpha = allowEdit ? 1 : 0, Action = () => RequestDeletion?.Invoke(Model), From 21d5f842fccb799ad83ae47ca868b4b3a9827cbb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Feb 2021 14:52:36 +0900 Subject: [PATCH 346/385] Re-layout to reduce movement --- .../Multiplayer/MultiplayerMatchSubScreen.cs | 95 +++++++++++-------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 86982e2794..4664ac6bfe 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -97,18 +97,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, new Drawable[] { - new GridContainer + new Container { RelativeSizeAxes = Axes.Both, - Content = new[] + Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, + Child = new GridContainer { - new Drawable[] + RelativeSizeAxes = Axes.Both, + Content = new[] { - new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 5, Vertical = 10 }, - Child = new GridContainer + // Main left column + new GridContainer { RelativeSizeAxes = Axes.Both, RowDimensions = new[] @@ -126,45 +127,57 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer }, } } - } - }, - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 5 }, - Spacing = new Vector2(0, 10), - Children = new[] + }, + // Main right column + new FillFlowContainer { - new FillFlowContainer + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + new FillFlowContainer { - new OverlinedHeader("Beatmap"), - new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } - } - }, - userModsSection = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new OverlinedHeader("Beatmap"), + new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } + } + }, + userModsSection = new FillFlowContainer { - new OverlinedHeader("Extra mods"), - new ModDisplay + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 10 }, + Children = new Drawable[] { - DisplayUnrankedText = false, - Current = UserMods - }, - new PurpleTriangleButton - { - RelativeSizeAxes = Axes.X, - Text = "Select", - Action = () => userModsSelectOverlay.Show() + new OverlinedHeader("Extra mods"), + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + new PurpleTriangleButton + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = 90, + Text = "Select", + Action = () => userModsSelectOverlay.Show() + }, + new ModDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + DisplayUnrankedText = false, + Current = UserMods, + Scale = new Vector2(0.8f), + }, + } + } } } } From 8bb13915152ef6c5ac4a96f684bdbece349409c8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Feb 2021 14:53:13 +0900 Subject: [PATCH 347/385] Fix inspection --- .../Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index 768dc6512c..e2c98c0aad 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer Client.AddUser(new User { Id = 0, - Username = $"User 0", + Username = "User 0", CurrentModeRank = RNG.Next(1, 100000), CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }); From 8295fb908174b78cdf7803a28e5b5be4ae5346e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Feb 2021 16:28:22 +0900 Subject: [PATCH 348/385] Implement mania constant speed mod --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 1 + .../Mods/ManiaModConstantSpeed.cs | 35 +++++++++++++++++++ .../UI/DrawableManiaRuleset.cs | 5 +++ .../UI/Scrolling/DrawableScrollingRuleset.cs | 8 ++--- 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 59c766fd84..4c729fef83 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -238,6 +238,7 @@ namespace osu.Game.Rulesets.Mania new ManiaModMirror(), new ManiaModDifficultyAdjust(), new ManiaModInvert(), + new ManiaModConstantSpeed() }; case ModType.Automation: diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs new file mode 100644 index 0000000000..078394b1d8 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModConstantSpeed : Mod, IApplicableToDrawableRuleset + { + public override string Name => "Constant Speed"; + + public override string Acronym => "CS"; + + public override double ScoreMultiplier => 1; + + public override string Description => "No more tricky speed changes!"; + + public override IconUsage? Icon => FontAwesome.Solid.Equals; + + public override ModType Type => ModType.Conversion; + + public override bool Ranked => false; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + var maniaRuleset = (DrawableManiaRuleset)drawableRuleset; + maniaRuleset.ScrollMethod = ScrollVisualisationMethod.Constant; + } + } +} diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 941ac9816c..6b34dbfa09 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; @@ -49,6 +50,10 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; + public ScrollVisualisationMethod ScrollMethod = ScrollVisualisationMethod.Sequential; + + protected override ScrollVisualisationMethod VisualisationMethod => ScrollMethod; + private readonly Bindable configDirection = new Bindable(); private readonly Bindable configTimeRange = new BindableDouble(); diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 0955f32790..6ffdad211b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -91,7 +91,11 @@ namespace osu.Game.Rulesets.UI.Scrolling scrollingInfo = new LocalScrollingInfo(); scrollingInfo.Direction.BindTo(Direction); scrollingInfo.TimeRange.BindTo(TimeRange); + } + [BackgroundDependencyLoader] + private void load() + { switch (VisualisationMethod) { case ScrollVisualisationMethod.Sequential: @@ -106,11 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling scrollingInfo.Algorithm = new ConstantScrollAlgorithm(); break; } - } - [BackgroundDependencyLoader] - private void load() - { double lastObjectTime = Objects.LastOrDefault()?.GetEndTime() ?? double.MaxValue; double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH; From c8f1126bd793cdf6169a1d27742fd0909cdd9c33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Feb 2021 19:44:39 +0900 Subject: [PATCH 349/385] Add failing test --- ...tion.cs => TestAPIModJsonSerialization.cs} | 2 +- .../TestAPIModMessagePackSerialization.cs | 139 ++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) rename osu.Game.Tests/Online/{TestAPIModSerialization.cs => TestAPIModJsonSerialization.cs} (99%) create mode 100644 osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs diff --git a/osu.Game.Tests/Online/TestAPIModSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs similarity index 99% rename from osu.Game.Tests/Online/TestAPIModSerialization.cs rename to osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 5948582d77..aa6f66da81 100644 --- a/osu.Game.Tests/Online/TestAPIModSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -16,7 +16,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Tests.Online { [TestFixture] - public class TestAPIModSerialization + public class TestAPIModJsonSerialization { [Test] public void TestAcronymIsPreserved() diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs new file mode 100644 index 0000000000..4294f89397 --- /dev/null +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -0,0 +1,139 @@ +// 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 MessagePack; +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Online.API; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Online +{ + [TestFixture] + public class TestAPIModMessagePackSerialization + { + [Test] + public void TestAcronymIsPreserved() + { + var apiMod = new APIMod(new TestMod()); + + var deserialized = MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(apiMod)); + + Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym)); + } + + [Test] + public void TestRawSettingIsPreserved() + { + var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } }); + + var deserialized = MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(apiMod)); + + Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0)); + } + + [Test] + public void TestConvertedModHasCorrectSetting() + { + var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } }); + + var deserialized = MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(apiMod)); + var converted = (TestMod)deserialized.ToMod(new TestRuleset()); + + Assert.That(converted.TestSetting.Value, Is.EqualTo(2)); + } + + [Test] + public void TestDeserialiseTimeRampMod() + { + // Create the mod with values different from default. + var apiMod = new APIMod(new TestModTimeRamp + { + AdjustPitch = { Value = false }, + InitialRate = { Value = 1.25 }, + FinalRate = { Value = 0.25 } + }); + + var deserialised = MessagePackSerializer.Deserialize(MessagePackSerializer.Serialize(apiMod)); + var converted = (TestModTimeRamp)deserialised.ToMod(new TestRuleset()); + + Assert.That(converted.AdjustPitch.Value, Is.EqualTo(false)); + Assert.That(converted.InitialRate.Value, Is.EqualTo(1.25)); + Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25)); + } + + private class TestRuleset : Ruleset + { + public override IEnumerable GetModsFor(ModType type) => new Mod[] + { + new TestMod(), + new TestModTimeRamp(), + }; + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new System.NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException(); + + public override string Description { get; } = string.Empty; + public override string ShortName { get; } = string.Empty; + } + + private class TestMod : Mod + { + public override string Name => "Test Mod"; + public override string Acronym => "TM"; + public override double ScoreMultiplier => 1; + + [SettingSource("Test")] + public BindableNumber TestSetting { get; } = new BindableDouble + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Precision = 0.01, + }; + } + + private class TestModTimeRamp : ModTimeRamp + { + public override string Name => "Test Mod"; + public override string Acronym => "TMTR"; + public override double ScoreMultiplier => 1; + + [SettingSource("Initial rate", "The starting speed of the track")] + public override BindableNumber InitialRate { get; } = new BindableDouble + { + MinValue = 1, + MaxValue = 2, + Default = 1.5, + Value = 1.5, + Precision = 0.01, + }; + + [SettingSource("Final rate", "The speed increase to ramp towards")] + public override BindableNumber FinalRate { get; } = new BindableDouble + { + MinValue = 0, + MaxValue = 1, + Default = 0.5, + Value = 0.5, + Precision = 0.01, + }; + + [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] + public override BindableBool AdjustPitch { get; } = new BindableBool + { + Default = true, + Value = true + }; + } + } +} From 75f1ebd5f92ef4200f21f457bea58a23d4cdfa70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Feb 2021 19:46:47 +0900 Subject: [PATCH 350/385] Add custom resolver for mod settings dictionary --- osu.Game/Online/API/APIMod.cs | 1 + .../API/ModSettingsDictionaryFormatter.cs | 96 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 osu.Game/Online/API/ModSettingsDictionaryFormatter.cs diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 69ce3825ee..bff08b0515 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -23,6 +23,7 @@ namespace osu.Game.Online.API [JsonProperty("settings")] [Key(1)] + [MessagePackFormatter(typeof(ModSettingsDictionaryFormatter))] public Dictionary Settings { get; set; } = new Dictionary(); [JsonConstructor] diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs new file mode 100644 index 0000000000..a8ee9beca5 --- /dev/null +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using MessagePack; +using MessagePack.Formatters; +using osu.Framework.Bindables; + +namespace osu.Game.Online.API +{ + public class ModSettingsDictionaryFormatter : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, Dictionary value, IFormatterResolver formatterResolver) + { + int startOffset = offset; + + offset += MessagePackBinary.WriteArrayHeader(ref bytes, offset, value.Count); + + foreach (var kvp in value) + { + offset += MessagePackBinary.WriteString(ref bytes, offset, kvp.Key); + + switch (kvp.Value) + { + case Bindable d: + offset += MessagePackBinary.WriteDouble(ref bytes, offset, d.Value); + break; + + case Bindable f: + offset += MessagePackBinary.WriteSingle(ref bytes, offset, f.Value); + break; + + case Bindable b: + offset += MessagePackBinary.WriteBoolean(ref bytes, offset, b.Value); + break; + + default: + throw new ArgumentException("A setting was of a type not supported by the messagepack serialiser", nameof(bytes)); + } + } + + return offset - startOffset; + } + + public Dictionary Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + int startOffset = offset; + + var output = new Dictionary(); + + int itemCount = MessagePackBinary.ReadArrayHeader(bytes, offset, out readSize); + offset += readSize; + + for (int i = 0; i < itemCount; i++) + { + var key = MessagePackBinary.ReadString(bytes, offset, out readSize); + offset += readSize; + + switch (MessagePackBinary.GetMessagePackType(bytes, offset)) + { + case MessagePackType.Float: + { + // could be either float or double... + // see https://github.com/msgpack/msgpack/blob/master/spec.md#serialization-type-to-format-conversion + switch (MessagePackCode.ToFormatName(bytes[offset])) + { + case "float 32": + output[key] = MessagePackBinary.ReadSingle(bytes, offset, out readSize); + offset += readSize; + break; + + case "float 64": + output[key] = MessagePackBinary.ReadDouble(bytes, offset, out readSize); + offset += readSize; + break; + + default: + throw new ArgumentException("A setting was of a type not supported by the messagepack deserialiser", nameof(bytes)); + } + + break; + } + + case MessagePackType.Boolean: + output[key] = MessagePackBinary.ReadBoolean(bytes, offset, out readSize); + offset += readSize; + break; + + default: + throw new ArgumentException("A setting was of a type not supported by the messagepack deserialiser", nameof(bytes)); + } + } + + readSize = offset - startOffset; + return output; + } + } +} From d3f056f188ee7410f7603d71b6abec964755231b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Feb 2021 20:06:25 +0900 Subject: [PATCH 351/385] Add missing licence header --- osu.Game/Online/API/ModSettingsDictionaryFormatter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index a8ee9beca5..1b381e7c98 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.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 System.Collections.Generic; using MessagePack; From 1380717ebb5b51da488736df8cd30484ecc62532 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Feb 2021 20:19:27 +0900 Subject: [PATCH 352/385] Use PrimitiveObjectFormatter to simplify code --- .../API/ModSettingsDictionaryFormatter.cs | 43 +++---------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index 1b381e7c98..3dd8bff61b 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -15,6 +15,8 @@ namespace osu.Game.Online.API { int startOffset = offset; + var primitiveFormatter = PrimitiveObjectFormatter.Instance; + offset += MessagePackBinary.WriteArrayHeader(ref bytes, offset, value.Count); foreach (var kvp in value) @@ -24,15 +26,15 @@ namespace osu.Game.Online.API switch (kvp.Value) { case Bindable d: - offset += MessagePackBinary.WriteDouble(ref bytes, offset, d.Value); + offset += primitiveFormatter.Serialize(ref bytes, offset, d.Value, formatterResolver); break; case Bindable f: - offset += MessagePackBinary.WriteSingle(ref bytes, offset, f.Value); + offset += primitiveFormatter.Serialize(ref bytes, offset, f.Value, formatterResolver); break; case Bindable b: - offset += MessagePackBinary.WriteBoolean(ref bytes, offset, b.Value); + offset += primitiveFormatter.Serialize(ref bytes, offset, b.Value, formatterResolver); break; default: @@ -57,39 +59,8 @@ namespace osu.Game.Online.API var key = MessagePackBinary.ReadString(bytes, offset, out readSize); offset += readSize; - switch (MessagePackBinary.GetMessagePackType(bytes, offset)) - { - case MessagePackType.Float: - { - // could be either float or double... - // see https://github.com/msgpack/msgpack/blob/master/spec.md#serialization-type-to-format-conversion - switch (MessagePackCode.ToFormatName(bytes[offset])) - { - case "float 32": - output[key] = MessagePackBinary.ReadSingle(bytes, offset, out readSize); - offset += readSize; - break; - - case "float 64": - output[key] = MessagePackBinary.ReadDouble(bytes, offset, out readSize); - offset += readSize; - break; - - default: - throw new ArgumentException("A setting was of a type not supported by the messagepack deserialiser", nameof(bytes)); - } - - break; - } - - case MessagePackType.Boolean: - output[key] = MessagePackBinary.ReadBoolean(bytes, offset, out readSize); - offset += readSize; - break; - - default: - throw new ArgumentException("A setting was of a type not supported by the messagepack deserialiser", nameof(bytes)); - } + output[key] = PrimitiveObjectFormatter.Instance.Deserialize(bytes, offset, formatterResolver, out readSize); + offset += readSize; } readSize = offset - startOffset; From e3d323989c6925304f1f8e126e3cab6aee31695a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Feb 2021 20:42:27 +0900 Subject: [PATCH 353/385] Switch to SignalR 5.0 and implement using better API --- .../API/ModSettingsDictionaryFormatter.cs | 39 ++++++++----------- osu.Game/osu.Game.csproj | 7 ++-- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index 3dd8bff61b..fc6b82a16b 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -1,8 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Buffers; using System.Collections.Generic; +using System.Text; using MessagePack; using MessagePack.Formatters; using osu.Framework.Bindables; @@ -11,59 +12,51 @@ namespace osu.Game.Online.API { public class ModSettingsDictionaryFormatter : IMessagePackFormatter> { - public int Serialize(ref byte[] bytes, int offset, Dictionary value, IFormatterResolver formatterResolver) + public void Serialize(ref MessagePackWriter writer, Dictionary value, MessagePackSerializerOptions options) { - int startOffset = offset; - var primitiveFormatter = PrimitiveObjectFormatter.Instance; - offset += MessagePackBinary.WriteArrayHeader(ref bytes, offset, value.Count); + writer.WriteArrayHeader(value.Count); foreach (var kvp in value) { - offset += MessagePackBinary.WriteString(ref bytes, offset, kvp.Key); + var stringBytes = new ReadOnlySequence(Encoding.UTF8.GetBytes(kvp.Key)); + writer.WriteString(in stringBytes); switch (kvp.Value) { case Bindable d: - offset += primitiveFormatter.Serialize(ref bytes, offset, d.Value, formatterResolver); + primitiveFormatter.Serialize(ref writer, d.Value, options); break; case Bindable f: - offset += primitiveFormatter.Serialize(ref bytes, offset, f.Value, formatterResolver); + primitiveFormatter.Serialize(ref writer, f.Value, options); break; case Bindable b: - offset += primitiveFormatter.Serialize(ref bytes, offset, b.Value, formatterResolver); + primitiveFormatter.Serialize(ref writer, b.Value, options); break; default: - throw new ArgumentException("A setting was of a type not supported by the messagepack serialiser", nameof(bytes)); + // fall back for non-bindable cases. + primitiveFormatter.Serialize(ref writer, kvp.Value, options); + break; } } - - return offset - startOffset; } - public Dictionary Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public Dictionary Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { - int startOffset = offset; - var output = new Dictionary(); - int itemCount = MessagePackBinary.ReadArrayHeader(bytes, offset, out readSize); - offset += readSize; + int itemCount = reader.ReadArrayHeader(); for (int i = 0; i < itemCount; i++) { - var key = MessagePackBinary.ReadString(bytes, offset, out readSize); - offset += readSize; - - output[key] = PrimitiveObjectFormatter.Instance.Deserialize(bytes, offset, formatterResolver, out readSize); - offset += readSize; + output[reader.ReadString()] = + PrimitiveObjectFormatter.Instance.Deserialize(ref reader, options); } - readSize = offset - startOffset; return output; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e2b506e187..eabd5c7d12 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,11 +20,12 @@ - - - + + + + From db3f9e7cbee3ff3c719d80fc2943420b82a828a2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 4 Feb 2021 02:20:18 +0300 Subject: [PATCH 354/385] Apply documentation suggestion --- osu.Game/Online/DownloadTrackingComposite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 188cb9be7a..69c6ebd07c 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -67,10 +67,10 @@ namespace osu.Game.Online } /// - /// Verifies that the given databased model is in a correct state to be considered available. + /// Checks that a database model matches the one expected to be downloaded. /// /// - /// In the case of multiplayer/playlists, this has to verify that the databased beatmap set with the selected beatmap matches what's online. + /// In the case of multiplayer/playlists, this has to check that the databased beatmap set with the selected beatmap matches what's online. /// /// The model in database. protected virtual bool VerifyDatabasedModel([NotNull] TModel databasedModel) => true; From 76cfeae7e9cf844996ac9c53a54a1756034d1562 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 15:10:56 +0900 Subject: [PATCH 355/385] Add support for Bindable int in config --- osu.Game/Online/API/ModSettingsDictionaryFormatter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index fc6b82a16b..99e87677fa 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -29,6 +29,10 @@ namespace osu.Game.Online.API primitiveFormatter.Serialize(ref writer, d.Value, options); break; + case Bindable i: + primitiveFormatter.Serialize(ref writer, i.Value, options); + break; + case Bindable f: primitiveFormatter.Serialize(ref writer, f.Value, options); break; From d165344070434f8e47af0d92c206b7a7fe9ee9ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 15:19:57 +0900 Subject: [PATCH 356/385] Force newer version of MessagePack for fixed iOS compatibility --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index eabd5c7d12..f866b232d8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,6 +20,7 @@ + From 30dae5bf1c1188139fb683e235b15b16af5e83ec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Feb 2021 15:17:47 +0900 Subject: [PATCH 357/385] Add test to make sure the algorithm is passed down in time --- .../Mods/TestSceneManiaModConstantSpeed.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModConstantSpeed.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModConstantSpeed.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModConstantSpeed.cs new file mode 100644 index 0000000000..60363aaeef --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModConstantSpeed.cs @@ -0,0 +1,31 @@ +// 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.Allocation; +using osu.Framework.Testing; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Rulesets.UI.Scrolling.Algorithms; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Mods +{ + public class TestSceneManiaModConstantSpeed : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + [Test] + public void TestConstantScroll() => CreateModTest(new ModTestData + { + Mod = new ManiaModConstantSpeed(), + PassCondition = () => + { + var hitObject = Player.ChildrenOfType().FirstOrDefault(); + return hitObject?.Dependencies.Get().Algorithm is ConstantScrollAlgorithm; + } + }); + } +} From a36b426b243e42062b93782ad5204dd35bd5ad88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 15:46:50 +0900 Subject: [PATCH 358/385] Force iOS back to previous versions of messagepack --- osu.iOS.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.iOS.props b/osu.iOS.props index dc3527c687..22d104f2e1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -80,6 +80,9 @@ + + + From b2f1e133f86d3cf26d9581cdb45c0beecbe2ab5f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 16:53:55 +0900 Subject: [PATCH 359/385] Allow checkbox nub to be moved to the left --- .../Graphics/UserInterface/OsuCheckbox.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 517f83daa9..313962d9c6 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -48,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; - public OsuCheckbox() + public OsuCheckbox(bool nubOnRight = true) { AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; @@ -61,17 +61,24 @@ namespace osu.Game.Graphics.UserInterface { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding } - }, - Nub = new Nub - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Margin = new MarginPadding { Right = nub_padding }, }, + Nub = new Nub(), new HoverClickSounds() }; + if (nubOnRight) + { + Nub.Anchor = Anchor.CentreRight; + Nub.Origin = Anchor.CentreRight; + Nub.Margin = new MarginPadding { Right = nub_padding }; + labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding }; + } + else + { + Nub.Margin = new MarginPadding { Left = nub_padding }; + labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding }; + } + Nub.Current.BindTo(Current); Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; From 3148bbda2a09c00f38aec261bcb4b78559c9412e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 16:54:17 +0900 Subject: [PATCH 360/385] Allow custom font to be used in OsuCheckbox --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 313962d9c6..61a42dac23 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; @@ -57,7 +58,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - labelText = new OsuTextFlowContainer + labelText = new OsuTextFlowContainer(ApplyLabelParameters) { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -84,6 +85,13 @@ namespace osu.Game.Graphics.UserInterface Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1; } + /// + /// A function which can be overridden to change the parameters of the label's text. + /// + protected virtual void ApplyLabelParameters(SpriteText text) + { + } + [BackgroundDependencyLoader] private void load(AudioManager audio) { From 48a58e790e2d2ec640d3273ee78e222ff6aed07e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 16:57:39 +0900 Subject: [PATCH 361/385] Don't specify arbitrary width --- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 5b9a19897f..c60afeb2aa 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.OnlinePlay protected override Drawable CreateHeader(string text) => new Container { AutoSizeAxes = Axes.Y, - Width = 175, + RelativeSizeAxes = Axes.X, Child = checkbox = new HeaderCheckbox { LabelText = text, From b32e10514d1f8f48d7e580947176a56faa497d5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 16:58:02 +0900 Subject: [PATCH 362/385] Fix padding on label text not being double-applied (meaning no padding between nub and text) --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 61a42dac23..b80941c0bc 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -72,12 +72,12 @@ namespace osu.Game.Graphics.UserInterface Nub.Anchor = Anchor.CentreRight; Nub.Origin = Anchor.CentreRight; Nub.Margin = new MarginPadding { Right = nub_padding }; - labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding }; + labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 }; } else { Nub.Margin = new MarginPadding { Left = nub_padding }; - labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding }; + labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 }; } Nub.Current.BindTo(Current); From daf7ab942274a71298ff4caf3505cee52381aed0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 16:58:15 +0900 Subject: [PATCH 363/385] Apply the expected font to the checkbox's label --- .../Screens/OnlinePlay/FreeModSelectOverlay.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index c60afeb2aa..180f6079ac 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -3,8 +3,11 @@ using System; using System.Linq; +using System.Reflection.Emit; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; @@ -120,6 +123,19 @@ namespace osu.Game.Screens.OnlinePlay protected override bool PlaySoundsOnUserChange => false; + public HeaderCheckbox() + : base(false) + + { + } + + protected override void ApplyLabelParameters(SpriteText text) + { + base.ApplyLabelParameters(text); + + text.Font = OsuFont.GetFont(weight: FontWeight.Bold); + } + protected override void OnUserChange(bool value) { base.OnUserChange(value); From 4bfe3aabdc6c165e2bd368b9a70ab45ec4cc39b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 17:06:11 +0900 Subject: [PATCH 364/385] Simplify sound debounce logic --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index c308dc2451..97902d1c15 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -496,17 +496,12 @@ namespace osu.Game.Overlays.Mods MultiplierLabel.FadeColour(Color4.White, 200); } - private ScheduledDelegate sampleOnDelegate; - private ScheduledDelegate sampleOffDelegate; - private void modButtonPressed(Mod selectedMod) { if (selectedMod != null) { - // Fixes buzzing when multiple mods are selected in the same frame. - sampleOnDelegate?.Cancel(); if (State.Value == Visibility.Visible) - sampleOnDelegate = Scheduler.Add(() => sampleOn?.Play()); + Scheduler.AddOnce(playSelectedSound); OnModSelected(selectedMod); @@ -514,15 +509,16 @@ namespace osu.Game.Overlays.Mods } else { - // Fixes buzzing when multiple mods are deselected in the same frame. - sampleOffDelegate?.Cancel(); if (State.Value == Visibility.Visible) - sampleOffDelegate = Scheduler.Add(() => sampleOff?.Play()); + Scheduler.AddOnce(playDeselectedSound); } refreshSelectedMods(); } + private void playSelectedSound() => sampleOn?.Play(); + private void playDeselectedSound() => sampleOff?.Play(); + /// /// Invoked when a new has been selected. /// From f23ca7c7cfd7bbd752e50464189c2c84546beba2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 18:10:55 +0900 Subject: [PATCH 365/385] Centralise selection animation logic --- osu.Game/Overlays/Mods/ModSection.cs | 51 +++++++++++++------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 1 - .../Overlays/Mods/SoloModSelectOverlay.cs | 2 +- .../OnlinePlay/FreeModSelectOverlay.cs | 25 +++++---- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 87a45ebf63..495b1c05cd 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -33,6 +33,8 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource modsLoadCts; + protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0; + /// /// True when all mod icons have completed loading. /// @@ -49,7 +51,11 @@ namespace osu.Game.Overlays.Mods return new ModButton(m) { - SelectionChanged = Action, + SelectionChanged = mod => + { + ModButtonStateChanged(mod); + Action?.Invoke(mod); + }, }; }).ToArray(); @@ -78,6 +84,10 @@ namespace osu.Game.Overlays.Mods } } + protected virtual void ModButtonStateChanged(Mod mod) + { + } + private ModButton[] buttons = Array.Empty(); protected override bool OnKeyDown(KeyDownEvent e) @@ -94,44 +104,53 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } + private const double initial_multiple_selection_delay = 100; + + private readonly Queue pendingSelectionOperations = new Queue(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + Scheduler.AddDelayed(() => + { + if (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) + dequeuedAction(); + }, initial_multiple_selection_delay, true); + } + /// /// Selects all mods. /// public void SelectAll() { + pendingSelectionOperations.Clear(); + foreach (var button in buttons.Where(b => !b.Selected)) - button.SelectAt(0); + pendingSelectionOperations.Enqueue(() => button.SelectAt(0)); } /// /// Deselects all mods. /// - /// Set to true to bypass animations and update selections immediately. - public void DeselectAll(bool immediate = false) => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null), immediate); + public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); /// /// Deselect one or more mods in this section. /// /// 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) { - int delay = 0; + pendingSelectionOperations.Clear(); foreach (var button in buttons) { - Mod selected = button.SelectedMod; - if (selected == null) continue; + if (button.SelectedMod == null) continue; foreach (var type in modTypes) { - if (type.IsInstanceOfType(selected)) - { - if (immediate) - button.Deselect(); - else - Scheduler.AddDelayed(button.Deselect, delay += 50); - } + if (type.IsInstanceOfType(button.SelectedMod)) + pendingSelectionOperations.Enqueue(button.Deselect); } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 97902d1c15..4f65a39ed3 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs index d039ad1f98..aa0e78c126 100644 --- a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Mods base.OnModSelected(mod); foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(mod.IncompatibleMods, true); + section.DeselectTypes(mod.IncompatibleMods); } } } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 180f6079ac..7bc226bb3f 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using System.Reflection.Emit; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -72,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay private void deselectAll() { foreach (var section in ModSectionsContainer.Children) - section.DeselectAll(true); + section.DeselectAll(); } protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); @@ -99,21 +98,21 @@ namespace osu.Game.Screens.OnlinePlay private void onCheckboxChanged(bool value) { - foreach (var button in ButtonsContainer.OfType()) - { - if (value) - button.SelectAt(0); - else - button.Deselect(); - } + if (value) + SelectAll(); + else + DeselectAll(); } - protected override void Update() + protected override void ModButtonStateChanged(Mod mod) { - base.Update(); + base.ModButtonStateChanged(mod); - var validButtons = ButtonsContainer.OfType().Where(b => b.Mod.HasImplementation); - checkbox.Current.Value = validButtons.All(b => b.Selected); + if (!SelectionAnimationRunning) + { + var validButtons = ButtonsContainer.OfType().Where(b => b.Mod.HasImplementation); + checkbox.Current.Value = validButtons.All(b => b.Selected); + } } } From 223b858227ed2a0d2aa3af284acbe581dbab84be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 18:56:40 +0900 Subject: [PATCH 366/385] Ramp the animation speed --- osu.Game/Overlays/Mods/ModSection.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 495b1c05cd..b9640a6026 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -104,19 +104,31 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } - private const double initial_multiple_selection_delay = 100; + private const double initial_multiple_selection_delay = 120; + + private double selectionDelay = initial_multiple_selection_delay; + private double lastSelection; private readonly Queue pendingSelectionOperations = new Queue(); - protected override void LoadComplete() + protected override void Update() { - base.LoadComplete(); + base.Update(); - Scheduler.AddDelayed(() => + if (selectionDelay == initial_multiple_selection_delay || Time.Current - lastSelection >= selectionDelay) { if (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) + { dequeuedAction(); - }, initial_multiple_selection_delay, true); + + selectionDelay = Math.Max(30, selectionDelay * 0.8f); + lastSelection = Time.Current; + } + else + { + selectionDelay = initial_multiple_selection_delay; + } + } } /// From a2674f3c3ff5a99d81127aae980c0ed05614fc47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 18:58:56 +0900 Subject: [PATCH 367/385] Add comments --- osu.Game/Overlays/Mods/ModSection.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index b9640a6026..353f779b4f 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -121,11 +121,14 @@ namespace osu.Game.Overlays.Mods { dequeuedAction(); + // each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements). selectionDelay = Math.Max(30, selectionDelay * 0.8f); lastSelection = Time.Current; } else { + // reset the selection delay after all animations have been completed. + // this will cause the next action to be immediately performed. selectionDelay = initial_multiple_selection_delay; } } From bf239f8bef321dbb90e61ebe8e453a13a22f11c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 19:12:37 +0900 Subject: [PATCH 368/385] Flush animation on closing mod overlay --- osu.Game/Overlays/Mods/ModSection.cs | 9 +++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 353f779b4f..440abfb1c0 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -231,5 +231,14 @@ namespace osu.Game.Overlays.Mods Font = OsuFont.GetFont(weight: FontWeight.Bold), Text = text }; + + /// + /// Play out all remaining animations immediately to leave mods in a good (final) state. + /// + public void FlushAnimation() + { + while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) + dequeuedAction(); + } } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 4f65a39ed3..93fe693937 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -379,6 +379,11 @@ namespace osu.Game.Overlays.Mods { base.PopOut(); + foreach (var section in ModSectionsContainer) + { + section.FlushAnimation(); + } + FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); From 15062cc63f71ae8ae64b9a5343da7585a5bb5fa0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 19:29:48 +0900 Subject: [PATCH 369/385] Fix intermittent test failures --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 92104cfc72..7f4dfaa9b0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -145,11 +145,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime)); - AddAssert("double time not visible", () => modSelect.ChildrenOfType().All(b => !b.Mods.Any(m => m is OsuModDoubleTime))); + AddUntilStep("double time not visible", () => modSelect.ChildrenOfType().All(b => !b.Mods.Any(m => m is OsuModDoubleTime))); AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); AddStep("make double time valid again", () => modSelect.IsValidMod = m => true); - AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); + AddUntilStep("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); } From 8f2f1a444f25058760583545c3390ce0562b3e5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 19:55:09 +0900 Subject: [PATCH 370/385] Avoid resetting selection on deselecting incompatibile types --- osu.Game/Overlays/Mods/ModSection.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 440abfb1c0..3f93eec7df 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -148,7 +148,11 @@ namespace osu.Game.Overlays.Mods /// /// Deselects all mods. /// - public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); + public void DeselectAll() + { + pendingSelectionOperations.Clear(); + DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); + } /// /// Deselect one or more mods in this section. @@ -156,8 +160,6 @@ namespace osu.Game.Overlays.Mods /// The types of s which should be deselected. public void DeselectTypes(IEnumerable modTypes) { - pendingSelectionOperations.Clear(); - foreach (var button in buttons) { if (button.SelectedMod == null) continue; From cef16a9f61e6a9550f80b81f5c3cf6a2603c45e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 19:55:15 +0900 Subject: [PATCH 371/385] Add test coverage of animation / selection flushing --- .../TestSceneModSelectOverlay.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 7f4dfaa9b0..44605f4994 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -46,6 +47,32 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("show", () => modSelect.Show()); } + [Test] + public void TestAnimationFlushOnClose() + { + changeRuleset(0); + + AddStep("Select all fun mods", () => + { + modSelect.ModSectionsContainer + .Single(c => c.ModType == ModType.DifficultyIncrease) + .SelectAll(); + }); + + AddUntilStep("many mods selected", () => modDisplay.Current.Value.Count >= 5); + + AddStep("trigger deselect and close overlay", () => + { + modSelect.ModSectionsContainer + .Single(c => c.ModType == ModType.DifficultyIncrease) + .DeselectAll(); + + modSelect.Hide(); + }); + + AddAssert("all mods deselected", () => modDisplay.Current.Value.Count == 0); + } + [Test] public void TestOsuMods() { @@ -312,6 +339,9 @@ namespace osu.Game.Tests.Visual.UserInterface public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); + public new FillFlowContainer ModSectionsContainer => + base.ModSectionsContainer; + public ModButton GetModButton(Mod mod) { var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); From f86f3236254019b99ae64e37b3628159c6758c94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 22:28:17 +0900 Subject: [PATCH 372/385] Add a basic guard against setting ScrollMethod too late in initialisation --- .../UI/DrawableManiaRuleset.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 6b34dbfa09..4ee060e91e 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -50,9 +51,21 @@ namespace osu.Game.Rulesets.Mania.UI protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; - public ScrollVisualisationMethod ScrollMethod = ScrollVisualisationMethod.Sequential; + public ScrollVisualisationMethod ScrollMethod + { + get => scrollMethod; + set + { + if (IsLoaded) + throw new InvalidOperationException($"Can't alter {nameof(ScrollMethod)} after ruleset is already loaded"); - protected override ScrollVisualisationMethod VisualisationMethod => ScrollMethod; + scrollMethod = value; + } + } + + private ScrollVisualisationMethod scrollMethod = ScrollVisualisationMethod.Sequential; + + protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod; private readonly Bindable configDirection = new Bindable(); private readonly Bindable configTimeRange = new BindableDouble(); From 794f9e5e932294d8c77e9cdc5b0b995b8f8d9062 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 22:53:41 +0900 Subject: [PATCH 373/385] Add missing centre anchor/origin --- osu.Game/Graphics/UserInterface/OsuCheckbox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index b80941c0bc..f6effa0834 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -76,6 +76,8 @@ namespace osu.Game.Graphics.UserInterface } else { + Nub.Anchor = Anchor.CentreLeft; + Nub.Origin = Anchor.CentreLeft; Nub.Margin = new MarginPadding { Left = nub_padding }; labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 }; } From 0750c3cb6a3ae9f8bc614fe61f082e936ba4f705 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Feb 2021 23:44:46 +0900 Subject: [PATCH 374/385] Add back immediate deselection flow to ensure user selections can occur without contention --- osu.Game/Overlays/Mods/ModSection.cs | 10 ++++++++-- osu.Game/Overlays/Mods/SoloModSelectOverlay.cs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 3f93eec7df..ecbcba7ad3 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -158,7 +158,8 @@ namespace osu.Game.Overlays.Mods /// Deselect one or more mods in this section. /// /// The types of s which should be deselected. - public void DeselectTypes(IEnumerable modTypes) + /// Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow. + public void DeselectTypes(IEnumerable modTypes, bool immediate = false) { foreach (var button in buttons) { @@ -167,7 +168,12 @@ namespace osu.Game.Overlays.Mods foreach (var type in modTypes) { if (type.IsInstanceOfType(button.SelectedMod)) - pendingSelectionOperations.Enqueue(button.Deselect); + { + if (immediate) + button.Deselect(); + else + pendingSelectionOperations.Enqueue(button.Deselect); + } } } } diff --git a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs index aa0e78c126..d039ad1f98 100644 --- a/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/SoloModSelectOverlay.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Mods base.OnModSelected(mod); foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(mod.IncompatibleMods); + section.DeselectTypes(mod.IncompatibleMods, true); } } } From 4e530d2eaf45fc3c1b890b1016d2a6cbcec8658b Mon Sep 17 00:00:00 2001 From: Joehu Date: Wed, 3 Feb 2021 21:14:27 -0800 Subject: [PATCH 375/385] Remove old alpha hack from nub fill --- osu.Game/Graphics/UserInterface/Nub.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 82b09e0821..18d8b880ea 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -42,13 +42,7 @@ namespace osu.Game.Graphics.UserInterface }, }; - Current.ValueChanged += filled => - { - if (filled.NewValue) - fill.FadeIn(200, Easing.OutQuint); - else - fill.FadeTo(0.01f, 200, Easing.OutQuint); //todo: remove once we figure why containers aren't drawing at all times - }; + Current.ValueChanged += filled => fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); } [BackgroundDependencyLoader] From 9ef130cdccd46535c40db44b01b16f9624b4e36f Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Feb 2021 13:28:35 -0800 Subject: [PATCH 376/385] Fix codefactor style issues --- osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs | 1 - osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs index 7308d6b499..8d8ee49af7 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/BorderPiece.cs @@ -29,4 +29,3 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default } } } - diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs index d160956a6e..c8895f32f4 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/HyperBorderPiece.cs @@ -19,4 +19,3 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default } } } - From d62bbbb7627673b4dc03729639a54ad9b864079e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 5 Feb 2021 00:38:56 +0300 Subject: [PATCH 377/385] Enhance documentation Co-authored-by: Dan Balasescu --- osu.Game/Online/DownloadTrackingComposite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index 69c6ebd07c..52042c266b 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -70,7 +70,7 @@ namespace osu.Game.Online /// Checks that a database model matches the one expected to be downloaded. /// /// - /// In the case of multiplayer/playlists, this has to check that the databased beatmap set with the selected beatmap matches what's online. + /// For online play, this could be used to check that the databased model matches the online beatmap. /// /// The model in database. protected virtual bool VerifyDatabasedModel([NotNull] TModel databasedModel) => true; From c0bd27fe832c0d05083107b0f488d7c979cce6d6 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 4 Feb 2021 13:55:15 -0800 Subject: [PATCH 378/385] Fix readme version badge not linking to correct page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index efca075042..e09b4d86a5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) -[![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)]() +[![GitHub release](https://img.shields.io/github/release/ppy/osu.svg)](https://github.com/ppy/osu/releases/latest) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) From a2fdba3e5173441147761ace92894f54cc6f309f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Feb 2021 12:24:38 +0900 Subject: [PATCH 379/385] Rename to OnlinePlayBeatmapAvailabilityTracker --- ...s => TestSceneOnlinePlayBeatmapAvailabilityTracker.cs} | 8 ++++---- ...racker.cs => OnlinePlayBeatmapAvailablilityTracker.cs} | 4 ++-- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game.Tests/Online/{TestSceneMultiplayerBeatmapAvailabilityTracker.cs => TestSceneOnlinePlayBeatmapAvailabilityTracker.cs} (96%) rename osu.Game/Online/Rooms/{MultiplayerBeatmapAvailablilityTracker.cs => OnlinePlayBeatmapAvailablilityTracker.cs} (95%) diff --git a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs similarity index 96% rename from osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs rename to osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 646c4139af..d3475de157 100644 --- a/osu.Game.Tests/Online/TestSceneMultiplayerBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -28,7 +28,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Tests.Online { [HeadlessTest] - public class TestSceneMultiplayerBeatmapAvailabilityTracker : OsuTestScene + public class TestSceneOnlinePlayBeatmapAvailabilityTracker : OsuTestScene { private RulesetStore rulesets; private TestBeatmapManager beatmaps; @@ -38,7 +38,7 @@ namespace osu.Game.Tests.Online private BeatmapSetInfo testBeatmapSet; private readonly Bindable selectedItem = new Bindable(); - private MultiplayerBeatmapAvailablilityTracker availablilityTracker; + private OnlinePlayBeatmapAvailablilityTracker availablilityTracker; [BackgroundDependencyLoader] private void load(AudioManager audio, GameHost host) @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Online Ruleset = { Value = testBeatmapInfo.Ruleset }, }; - Child = availablilityTracker = new MultiplayerBeatmapAvailablilityTracker + Child = availablilityTracker = new OnlinePlayBeatmapAvailablilityTracker { SelectedItem = { BindTarget = selectedItem, } }; @@ -118,7 +118,7 @@ namespace osu.Game.Tests.Online }); addAvailabilityCheckStep("state still not downloaded", BeatmapAvailability.NotDownloaded); - AddStep("recreate tracker", () => Child = availablilityTracker = new MultiplayerBeatmapAvailablilityTracker + AddStep("recreate tracker", () => Child = availablilityTracker = new OnlinePlayBeatmapAvailablilityTracker { SelectedItem = { BindTarget = selectedItem } }); diff --git a/osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs similarity index 95% rename from osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs rename to osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs index 578c4db2f8..ad4b3c5151 100644 --- a/osu.Game/Online/Rooms/MultiplayerBeatmapAvailablilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailablilityTracker.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class MultiplayerBeatmapAvailablilityTracker : DownloadTrackingComposite + public class OnlinePlayBeatmapAvailablilityTracker : DownloadTrackingComposite { public readonly IBindable SelectedItem = new Bindable(); @@ -26,7 +26,7 @@ namespace osu.Game.Online.Rooms private readonly Bindable availability = new Bindable(); - public MultiplayerBeatmapAvailablilityTracker() + public OnlinePlayBeatmapAvailablilityTracker() { State.BindValueChanged(_ => updateAvailability()); Progress.BindValueChanged(_ => updateAvailability(), true); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index cdf889e4f1..b1b3dde26e 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -41,11 +41,11 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected MultiplayerBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } + protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } protected RoomSubScreen() { - BeatmapAvailablilityTracker = new MultiplayerBeatmapAvailablilityTracker + BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker { SelectedItem = { BindTarget = SelectedItem }, }; From 85e63afcb48288f7838409c5b14380507ea3443d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Feb 2021 12:36:25 +0900 Subject: [PATCH 380/385] Rename Mods -> RequiredMods --- osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs | 6 +++--- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs index d0e19d9f37..4fb9d724b5 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Online.Multiplayer [NotNull] [Key(4)] - public IEnumerable Mods { get; set; } = Enumerable.Empty(); + public IEnumerable RequiredMods { get; set; } = Enumerable.Empty(); [NotNull] [Key(5)] @@ -39,14 +39,14 @@ namespace osu.Game.Online.Multiplayer public bool Equals(MultiplayerRoomSettings other) => BeatmapID == other.BeatmapID && BeatmapChecksum == other.BeatmapChecksum - && Mods.SequenceEqual(other.Mods) + && RequiredMods.SequenceEqual(other.RequiredMods) && AllowedMods.SequenceEqual(other.AllowedMods) && RulesetID == other.RulesetID && Name.Equals(other.Name, StringComparison.Ordinal); public override string ToString() => $"Name:{Name}" + $" Beatmap:{BeatmapID} ({BeatmapChecksum})" - + $" Mods:{string.Join(',', Mods)}" + + $" RequiredMods:{string.Join(',', RequiredMods)}" + $" AllowedMods:{string.Join(',', AllowedMods)}" + $" Ruleset:{RulesetID}"; } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index 69df2a69cc..aedbe37d56 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -192,7 +192,7 @@ namespace osu.Game.Online.Multiplayer BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID, BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash, RulesetID = item.GetOr(existingPlaylistItem).RulesetID, - Mods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods, + RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods, AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods }); } @@ -531,7 +531,7 @@ namespace osu.Game.Online.Multiplayer beatmap.MD5Hash = settings.BeatmapChecksum; var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance(); - var mods = settings.Mods.Select(m => m.ToMod(ruleset)); + var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset)); var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset)); PlaylistItem playlistItem = new PlaylistItem From 2e85ce5b824eeafe85f3b9b058b89601690314ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Feb 2021 12:40:16 +0900 Subject: [PATCH 381/385] Rename UserMods -> Mods for MultiplayerRoomUser --- osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs | 2 +- osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs | 2 +- .../OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs index 3271133bcd..c654127b94 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerRoomUser.cs @@ -36,7 +36,7 @@ namespace osu.Game.Online.Multiplayer /// [Key(3)] [NotNull] - public IEnumerable UserMods { get; set; } = Enumerable.Empty(); + public IEnumerable Mods { get; set; } = Enumerable.Empty(); [IgnoreMember] public User? User { get; set; } diff --git a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs index aedbe37d56..f7c9193dfe 100644 --- a/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/StatefulMultiplayerClient.cs @@ -400,7 +400,7 @@ namespace osu.Game.Online.Multiplayer if (user == null) return; - user.UserMods = mods; + user.Mods = mods; RoomUpdated?.Invoke(); }, false); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 8036e5f702..2983d1268d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance(); userStateDisplay.Status = User.State; - userModsDisplay.Current.Value = User.UserMods.Select(m => m.ToMod(ruleset)).ToList(); + userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList(); if (Room.Host?.Equals(User) == true) crown.FadeIn(fade_time); From 8004c19a8037bb4faece8646d604b1cb3c13ba97 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Feb 2021 12:40:42 +0900 Subject: [PATCH 382/385] Remove ModValidationTest --- osu.Game.Tests/Mods/ModValidationTest.cs | 64 ------------------------ 1 file changed, 64 deletions(-) delete mode 100644 osu.Game.Tests/Mods/ModValidationTest.cs diff --git a/osu.Game.Tests/Mods/ModValidationTest.cs b/osu.Game.Tests/Mods/ModValidationTest.cs deleted file mode 100644 index 991adc221e..0000000000 --- a/osu.Game.Tests/Mods/ModValidationTest.cs +++ /dev/null @@ -1,64 +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 Moq; -using NUnit.Framework; -using osu.Game.Rulesets.Mods; -using osu.Game.Utils; - -namespace osu.Game.Tests.Mods -{ - [TestFixture] - public class ModValidationTest - { - [Test] - public void TestModIsCompatibleByItself() - { - var mod = new Mock(); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object })); - } - - [Test] - public void TestIncompatibleThroughTopLevel() - { - var mod1 = new Mock(); - var mod2 = new Mock(); - - mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() }); - - // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, mod2.Object }), Is.False); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod2.Object, mod1.Object }), Is.False); - } - - [Test] - public void TestIncompatibleThroughMultiMod() - { - var mod1 = new Mock(); - - // The nested mod. - var mod2 = new Mock(); - mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() }); - - var multiMod = new MultiMod(new MultiMod(mod2.Object)); - - // Test both orderings. - Assert.That(ModUtils.CheckCompatibleSet(new[] { multiMod, mod1.Object }), Is.False); - Assert.That(ModUtils.CheckCompatibleSet(new[] { mod1.Object, multiMod }), Is.False); - } - - [Test] - public void TestAllowedThroughMostDerivedType() - { - var mod = new Mock(); - Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() })); - } - - [Test] - public void TestNotAllowedThroughBaseType() - { - var mod = new Mock(); - Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False); - } - } -} From df2da5950f2f7b1fd233f75900e3979a362204e3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Feb 2021 13:05:11 +0900 Subject: [PATCH 383/385] Add back vertical spacer --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 4664ac6bfe..0466c8209f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -104,6 +104,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Child = new GridContainer { RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), + new Dimension(), + new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), + }, Content = new[] { new Drawable[] @@ -128,6 +134,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } }, + // Spacer + null, // Main right column new FillFlowContainer { From fc37d8b7df27a398f0343fcefbb6f15ce3b9450f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Feb 2021 14:25:19 +0900 Subject: [PATCH 384/385] Refactor content redirection logic to be easier to parse --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 19b68ee6d6..722b167387 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -45,18 +45,21 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached] protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } - private readonly Container content = new Container { RelativeSizeAxes = Axes.Both }; + private readonly Container content; protected RoomSubScreen() { - BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker + InternalChildren = new Drawable[] { - SelectedItem = { BindTarget = SelectedItem } + BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker + { + SelectedItem = { BindTarget = SelectedItem } + }, + content = new Container { RelativeSizeAxes = Axes.Both }, }; - - base.AddInternal(content); } + // Forward all internal management to content to ensure locally added components are not removed unintentionally. // This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690) protected override void AddInternal(Drawable drawable) => content.Add(drawable); protected override bool RemoveInternal(Drawable drawable) => content.Remove(drawable); @@ -65,8 +68,6 @@ namespace osu.Game.Screens.OnlinePlay.Match [BackgroundDependencyLoader] private void load(AudioManager audio) { - base.AddInternal(BeatmapAvailablilityTracker); - sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); } From de8724b1f6b7d714260589dab5f5619afea0ca6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Feb 2021 14:39:25 +0900 Subject: [PATCH 385/385] Use AddRangeInternal for simplicity, but disallow ClearInternal for safety --- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 21 +++++-------------- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- .../Playlists/PlaylistsRoomSubScreen.cs | 4 ++-- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 722b167387..8be0e89665 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -7,8 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -45,25 +43,16 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached] protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } - private readonly Container content; - protected RoomSubScreen() { - InternalChildren = new Drawable[] + AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker { - BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker - { - SelectedItem = { BindTarget = SelectedItem } - }, - content = new Container { RelativeSizeAxes = Axes.Both }, - }; + SelectedItem = { BindTarget = SelectedItem } + }); } - // Forward all internal management to content to ensure locally added components are not removed unintentionally. - // This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690) - protected override void AddInternal(Drawable drawable) => content.Add(drawable); - protected override bool RemoveInternal(Drawable drawable) => content.Remove(drawable); - protected override void ClearInternal(bool disposeChildren = true) => content.Clear(disposeChildren); + protected override void ClearInternal(bool disposeChildren = true) => + throw new InvalidOperationException($"{nameof(RoomSubScreen)}'s children should not be cleared as it will remove required components"); [BackgroundDependencyLoader] private void load(AudioManager audio) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 8cc2e9061b..e1524067da 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + AddRangeInternal(new Drawable[] { mainContent = new GridContainer { @@ -177,7 +177,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer RelativeSizeAxes = Axes.Both, State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden } } - }; + }); if (client.Room == null) { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 745e5a58bf..0b8026044b 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + AddRangeInternal(new Drawable[] { mainContent = new GridContainer { @@ -190,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists EditPlaylist = () => this.Push(new MatchSongSelect()), State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } } - }; + }); if (roomId.Value == null) {