From 5d5b1e1f0e441de05c7c5fda27399d05a84415f9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 8 May 2021 11:00:22 +0200 Subject: [PATCH 01/49] Add StableImportManager --- osu.Game/Database/StableImportManager.cs | 92 +++++++++++++++++++ .../StableDirectorySelectScreen.cs | 42 +++++++++ 2 files changed, 134 insertions(+) create mode 100644 osu.Game/Database/StableImportManager.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs new file mode 100644 index 0000000000..46c6aab2fe --- /dev/null +++ b/osu.Game/Database/StableImportManager.cs @@ -0,0 +1,92 @@ +// 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.Text; +using System.Threading.Tasks; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Graphics; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Collections; +using osu.Game.IO; +using osu.Game.Overlays.Settings.Sections.Maintenance; +using osu.Game.Scoring; +using osu.Game.Skinning; + +namespace osu.Game.Database +{ + public class StableImportManager : Component + { + [Resolved] + private SkinManager skins { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + [Resolved] + private ScoreManager scores { get; set; } + + [Resolved] + private CollectionManager collections { get; set; } + + [Resolved] + private OsuGame game { get; set; } + + [Resolved(CanBeNull = true)] + private DesktopGameHost desktopGameHost { get; set; } + + private StableStorage cachedStorage; + + public async Task ImportFromStableAsync(StableContent content) + { + //var stableStorage = await getStableStorage().ConfigureAwait(false); + var importTasks = new List(); + + if (content.HasFlagFast(StableContent.Beatmaps)) + importTasks.Add(beatmaps.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Collections)) + importTasks.Add(collections.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Scores)) + importTasks.Add(scores.ImportFromStableAsync()); + + if (content.HasFlagFast(StableContent.Skins)) + importTasks.Add(skins.ImportFromStableAsync()); + + await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); + } + + private async Task getStableStorage() + { + var stableStorage = game.GetStorageForStableInstall(); + if (stableStorage != null) + return stableStorage; + + if (cachedStorage != null) + return cachedStorage; + + var taskCompletionSource = new TaskCompletionSource(); + Schedule(() => game.PerformFromScreen(t => t.Push(new StableDirectorySelectScreen(taskCompletionSource)))); + var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); + + return cachedStorage = new StableStorage(stablePath, desktopGameHost); + } + + } + + [Flags] + public enum StableContent + { + Beatmaps = 0x1, + Scores = 0x2, + Skins = 0x3, + Collections = 0x4, + All = Beatmaps | Scores | Skins | Collections + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs new file mode 100644 index 0000000000..d935bcf526 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Screens; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class StableDirectorySelectScreen : DirectorySelectScreen + { + private readonly TaskCompletionSource taskCompletionSource; + + protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; + + protected override OsuSpriteText CreateHeader() => new OsuSpriteText + { + Text = "Please select stable location", + Font = OsuFont.Default.With(size: 40) + }; + + public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) + { + this.taskCompletionSource = taskCompletionSource; + } + + protected override void OnSelection(DirectoryInfo directory) + { + taskCompletionSource.TrySetResult(directory.FullName); + this.Exit(); + } + + public override bool OnBackButton() + { + taskCompletionSource.TrySetCanceled(); + return base.OnBackButton(); + } + } +} From 851e33fd1542c21dea6717c4416db71ab1866f36 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 17:12:58 +0200 Subject: [PATCH 02/49] Hook up StableImportManager. --- osu.Game/Collections/CollectionManager.cs | 12 ++++----- osu.Game/Database/ArchiveModelManager.cs | 5 +--- osu.Game/Database/StableImportManager.cs | 25 ++++++++++--------- osu.Game/OsuGame.cs | 4 +++ .../Sections/Maintenance/GeneralSettings.cs | 19 +++++++------- osu.Game/Screens/Select/SongSelect.cs | 13 +++------- 6 files changed, 36 insertions(+), 42 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 9723409c79..fbd12cf672 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.IO; using osu.Game.IO.Legacy; using osu.Game.Overlays.Notifications; @@ -38,8 +39,6 @@ namespace osu.Game.Collections public readonly BindableList Collections = new BindableList(); - public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - [Resolved] private GameHost host { get; set; } @@ -104,17 +103,16 @@ namespace osu.Game.Collections /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public Task ImportFromStableAsync() + public Task ImportFromStableAsync(StableStorage stableStorage) { - var stable = GetStableStorage?.Invoke(); - if (stable == null) + if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); return Task.CompletedTask; } - if (!stable.Exists(database_name)) + if (!stableStorage.Exists(database_name)) { // This handles situations like when the user does not have a collections.db file Logger.Log($"No {database_name} available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); @@ -123,7 +121,7 @@ namespace osu.Game.Collections return Task.Run(async () => { - using (var stream = stable.GetStream(database_name)) + using (var stream = stableStorage.GetStream(database_name)) await Import(stream).ConfigureAwait(false); }); } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e0f80d2743..38a6af4654 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -81,8 +81,6 @@ namespace osu.Game.Database public virtual IEnumerable HandledExtensions => new[] { ".zip" }; - public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop; - protected readonly FileStore Files; protected readonly IDatabaseContextFactory ContextFactory; @@ -700,9 +698,8 @@ namespace osu.Game.Database /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// - public Task ImportFromStableAsync() + public Task ImportFromStableAsync(StableStorage stableStorage) { - var stableStorage = GetStableStorage?.Invoke(); if (stableStorage == null) { diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 46c6aab2fe..8baf03b7a3 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -3,9 +3,8 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; -using JetBrains.Annotations; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; @@ -19,7 +18,7 @@ using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Database -{ +{ public class StableImportManager : Component { [Resolved] @@ -42,22 +41,24 @@ namespace osu.Game.Database private StableStorage cachedStorage; + public bool SupportsImportFromStable => RuntimeInfo.IsDesktop; + public async Task ImportFromStableAsync(StableContent content) { - //var stableStorage = await getStableStorage().ConfigureAwait(false); + var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); if (content.HasFlagFast(StableContent.Beatmaps)) - importTasks.Add(beatmaps.ImportFromStableAsync()); + importTasks.Add(beatmaps.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Collections)) - importTasks.Add(collections.ImportFromStableAsync()); + importTasks.Add(collections.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Scores)) - importTasks.Add(scores.ImportFromStableAsync()); + importTasks.Add(scores.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Skins)) - importTasks.Add(skins.ImportFromStableAsync()); + importTasks.Add(skins.ImportFromStableAsync(stableStorage)); await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } @@ -83,10 +84,10 @@ namespace osu.Game.Database [Flags] public enum StableContent { - Beatmaps = 0x1, - Scores = 0x2, - Skins = 0x3, - Collections = 0x4, + Beatmaps = 1, + Scores = 2, + Skins = 4, + Collections = 8, All = Beatmaps | Scores | Skins | Collections } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f860cd8dd2..1cfe8ace43 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -100,6 +100,9 @@ namespace osu.Game [Cached] private readonly DifficultyRecommender difficultyRecommender = new DifficultyRecommender(); + [Cached] + private readonly StableImportManager stableImportManager = new StableImportManager(); + [Cached] private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); @@ -694,6 +697,7 @@ namespace osu.Game }, Add, true); loadComponentSingleFile(difficultyRecommender, Add); + loadComponentSingleFile(stableImportManager, Add); loadComponentSingleFile(screenshotManager, Add); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 848ce381a9..9bd360679e 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Collections; +using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Scoring; using osu.Game.Skinning; @@ -29,9 +30,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (beatmaps.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importBeatmapsButton = new SettingsButton { @@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importBeatmapsButton.Enabled.Value = false; - beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Beatmaps).ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); } }); } @@ -57,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (scores.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importScoresButton = new SettingsButton { @@ -65,7 +66,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importScoresButton.Enabled.Value = false; - scores.ImportFromStableAsync().ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Scores).ContinueWith(t => Schedule(() => importScoresButton.Enabled.Value = true)); } }); } @@ -83,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (skins.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importSkinsButton = new SettingsButton { @@ -91,7 +92,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importSkinsButton.Enabled.Value = false; - skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Skins).ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true)); } }); } @@ -111,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (collectionManager.SupportsImportFromStable) + if (stableImportManager.SupportsImportFromStable) { Add(importCollectionsButton = new SettingsButton { @@ -119,7 +120,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Action = () => { importCollectionsButton.Enabled.Value = false; - collectionManager.ImportFromStableAsync().ContinueWith(t => Schedule(() => importCollectionsButton.Enabled.Value = true)); + stableImportManager.ImportFromStableAsync(StableContent.Collections).ContinueWith(t => Schedule(() => importCollectionsButton.Enabled.Value = true)); } }); } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 215700d87c..ff72f36c75 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -22,7 +22,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Select.Options; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -35,9 +34,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Collections; using osu.Game.Graphics.UserInterface; -using osu.Game.Scoring; using System.Diagnostics; using osu.Game.Screens.Play; +using osu.Game.Database; namespace osu.Game.Screens.Select { @@ -101,7 +100,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores, CollectionManager collections, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, StableImportManager stableImportManager, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); @@ -287,13 +286,7 @@ namespace osu.Game.Screens.Select { dialogOverlay.Push(new ImportFromStablePopup(() => { - Task.Run(beatmaps.ImportFromStableAsync) - .ContinueWith(_ => - { - Task.Run(scores.ImportFromStableAsync); - Task.Run(collections.ImportFromStableAsync); - }, TaskContinuationOptions.OnlyOnRanToCompletion); - Task.Run(skins.ImportFromStableAsync); + Task.Run(() => stableImportManager.ImportFromStableAsync(StableContent.All)); })); } }); From 325a689d6553b9988c7c4378103135d960600376 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 18:15:21 +0200 Subject: [PATCH 03/49] Order imports depending on beatmap imports if any is running. --- osu.Game/Database/StableImportManager.cs | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 8baf03b7a3..24b8aa9f6a 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -48,18 +48,29 @@ namespace osu.Game.Database var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); + Task beatmapImportTask = default; if (content.HasFlagFast(StableContent.Beatmaps)) - importTasks.Add(beatmaps.ImportFromStableAsync(stableStorage)); - - if (content.HasFlagFast(StableContent.Collections)) - importTasks.Add(collections.ImportFromStableAsync(stableStorage)); - - if (content.HasFlagFast(StableContent.Scores)) - importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask = beatmaps.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Skins)) importTasks.Add(skins.ImportFromStableAsync(stableStorage)); + if (content.HasFlagFast(StableContent.Collections)) + { + if (beatmapImportTask != null) + importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); + else + importTasks.Add(collections.ImportFromStableAsync(stableStorage)); + } + + if (content.HasFlagFast(StableContent.Scores)) + { + if (beatmapImportTask != null) + importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); + else + importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + } + await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } From 481b0a0125c68f311d4678f910f3cbf47bb3fef1 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 19:50:43 +0200 Subject: [PATCH 04/49] Add StableDirectoryLocationDialog --- osu.Game/Database/StableImportManager.cs | 10 ++--- .../StableDirectoryLocationDialog.cs | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 24b8aa9f6a..09a08920dd 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -9,16 +9,16 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; -using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.IO; +using osu.Game.Overlays; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Scoring; using osu.Game.Skinning; namespace osu.Game.Database -{ +{ public class StableImportManager : Component { [Resolved] @@ -34,7 +34,7 @@ namespace osu.Game.Database private CollectionManager collections { get; set; } [Resolved] - private OsuGame game { get; set; } + private DialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private DesktopGameHost desktopGameHost { get; set; } @@ -83,8 +83,8 @@ namespace osu.Game.Database if (cachedStorage != null) return cachedStorage; - var taskCompletionSource = new TaskCompletionSource(); - Schedule(() => game.PerformFromScreen(t => t.Push(new StableDirectorySelectScreen(taskCompletionSource)))); + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource)); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); return cachedStorage = new StableStorage(stablePath, desktopGameHost); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs new file mode 100644 index 0000000000..273ee5dc89 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays.Settings.Sections.Maintenance +{ + public class StableDirectoryLocationDialog : PopupDialog + { + [Resolved] + private OsuGame game { get; set; } + + public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) + { + HeaderText = "Failed to automatically locate a stable installation."; + BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; + Icon = FontAwesome.Solid.QuestionCircle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = "Sure! I know where it is located!", + Action = () => Schedule(() => game.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) + }, + new PopupDialogCancelButton + { + Text = "Actually I don't have osu!stable installed.", + Action = () => taskCompletionSource.TrySetCanceled() + } + }; + } + } +} From 8ba50b185445358419ed2b01508d4859e9273e8c Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 19:52:26 +0200 Subject: [PATCH 05/49] Bring back injected dependency incorrectly marked as unused. --- osu.Game/Database/StableImportManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 09a08920dd..e4c89062f2 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -33,6 +33,9 @@ namespace osu.Game.Database [Resolved] private CollectionManager collections { get; set; } + [Resolved] + private OsuGame game { get; set; } + [Resolved] private DialogOverlay dialogOverlay { get; set; } From a7b740fd1de6cd6211c19d52697925f6b86e79fa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 9 May 2021 21:29:41 +0200 Subject: [PATCH 06/49] Reword ImportFromStablePopup and display the popup regardless of whether a stable install is detected. --- osu.Game/Database/StableImportManager.cs | 2 +- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index e4c89062f2..475077c54e 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -87,7 +87,7 @@ namespace osu.Game.Database return cachedStorage; var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource)); + Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); return cachedStorage = new StableStorage(stablePath, desktopGameHost); diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 8dab83b24c..e3a1505518 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; + BodyText = "You can import files from over a stable install, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; Icon = FontAwesome.Solid.Plane; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ff72f36c75..d8ad752151 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -281,8 +281,8 @@ namespace osu.Game.Screens.Select { Schedule(() => { - // if we have no beatmaps but osu-stable is found, let's prompt the user to import. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && beatmaps.StableInstallationAvailable) + // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable) { dialogOverlay.Push(new ImportFromStablePopup(() => { From dabe8bd4c7f358e19edc3ac57130093c170d6d64 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 10 May 2021 12:21:51 +0200 Subject: [PATCH 07/49] Fix code inspections and remove now unused code. --- osu.Game/Collections/CollectionManager.cs | 7 ------- osu.Game/Database/ArchiveModelManager.cs | 12 ------------ osu.Game/Database/StableImportManager.cs | 13 ++++--------- osu.Game/OsuGame.cs | 4 ---- .../Maintenance/StableDirectorySelectScreen.cs | 2 +- 5 files changed, 5 insertions(+), 33 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index fbd12cf672..e707b463cb 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -95,17 +94,11 @@ namespace osu.Game.Collections /// public Action PostNotification { protected get; set; } - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - /// /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 38a6af4654..93e2880ba0 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Humanizer; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; @@ -667,16 +666,6 @@ namespace osu.Game.Database #region osu-stable import - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - - /// - /// Denotes whether an osu-stable installation is present to perform automated imports from. - /// - public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; - /// /// The relative path from osu-stable's data directory to import items from. /// @@ -700,7 +689,6 @@ namespace osu.Game.Database /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) { Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 475077c54e..6f8225519d 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -60,18 +60,14 @@ namespace osu.Game.Database if (content.HasFlagFast(StableContent.Collections)) { - if (beatmapImportTask != null) - importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); - else - importTasks.Add(collections.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask?.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) + ?? collections.ImportFromStableAsync(stableStorage)); } if (content.HasFlagFast(StableContent.Scores)) { - if (beatmapImportTask != null) - importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); - else - importTasks.Add(scores.ImportFromStableAsync(stableStorage)); + importTasks.Add(beatmapImportTask?.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) + ?? scores.ImportFromStableAsync(stableStorage)); } await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); @@ -92,7 +88,6 @@ namespace osu.Game.Database return cachedStorage = new StableStorage(stablePath, desktopGameHost); } - } [Flags] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1cfe8ace43..06e0b6e9bf 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -569,14 +569,11 @@ namespace osu.Game // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications.Post(n); - SkinManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PostNotification = n => notifications.Post(n); - BeatmapManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); ScoreManager.PostNotification = n => notifications.Post(n); - ScoreManager.GetStableStorage = GetStorageForStableInstall; ScoreManager.PresentImport = items => PresentScore(items.First()); // make config aware of how to lookup skins for on-screen display purposes. @@ -693,7 +690,6 @@ namespace osu.Game loadComponentSingleFile(new CollectionManager(Storage) { PostNotification = n => notifications.Post(n), - GetStableStorage = GetStorageForStableInstall }, Add, true); loadComponentSingleFile(difficultyRecommender, Add); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index d935bcf526..6065122545 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } protected override void OnSelection(DirectoryInfo directory) - { + { taskCompletionSource.TrySetResult(directory.FullName); this.Exit(); } From e15e8068d3bd33170fd19497f2d2d3a4a73f8365 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 12:09:37 +0200 Subject: [PATCH 08/49] Reword StableDirectoryLocationDialog. Co-authored-by: Dean Herbert --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 273ee5dc89..298f7d2433 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { - HeaderText = "Failed to automatically locate a stable installation."; + HeaderText = "Failed to automatically locate an osu!stable installation."; BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; Icon = FontAwesome.Solid.QuestionCircle; From f60dbbfbbd2b53cb0d1207b82333b89bbb77ceef Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 12:10:26 +0200 Subject: [PATCH 09/49] Reword import dialogs. Co-authored-by: Dean Herbert --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- osu.Game/Screens/Select/ImportFromStablePopup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 298f7d2433..904c9deaae 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { HeaderText = "Failed to automatically locate an osu!stable installation."; - BodyText = "osu! failed to automatically locate a stable installation. Maybe you can tell osu! where it is located?"; + BodyText = "An existing install could not be located. If you know where it is, you can help locate it."; Icon = FontAwesome.Solid.QuestionCircle; Buttons = new PopupDialogButton[] diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index e3a1505518..d8137432bd 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "You can import files from over a stable install, though.\nWould you like to import your beatmaps, skins, collections and scores?\nThis will create a second copy of all files on disk."; + BodyText = "Would you like to import your beatmaps, skins, collections and scores from an existing osu!stable installation?\nThis will create a second copy of all files on disk."; Icon = FontAwesome.Solid.Plane; From bec06cfac7827423bbea001dd9cd5fd039b8bb09 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Thu, 13 May 2021 15:17:33 +0200 Subject: [PATCH 10/49] Reword `StableDirectoryLocationDialog` header Co-authored-by: Salman Ahmed --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 6065122545..f7c7934c63 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override OsuSpriteText CreateHeader() => new OsuSpriteText { - Text = "Please select stable location", + Text = "Please select your osu!stable install location", Font = OsuFont.Default.With(size: 40) }; From 41fafdf643f1a3858907559ff4bddbc5fa538f00 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Fri, 14 May 2021 17:24:36 +0200 Subject: [PATCH 11/49] Remove now unreachable code paths. --- osu.Game/Collections/CollectionManager.cs | 6 ------ osu.Game/Database/ArchiveModelManager.cs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index e707b463cb..3a63587b30 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -99,12 +99,6 @@ namespace osu.Game.Collections /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; - } - if (!stableStorage.Exists(database_name)) { // This handles situations like when the user does not have a collections.db file diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 93e2880ba0..550daf36b5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -689,12 +689,6 @@ namespace osu.Game.Database /// public Task ImportFromStableAsync(StableStorage stableStorage) { - if (stableStorage == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return Task.CompletedTask; - } - var storage = PrepareStableStorage(stableStorage); if (!storage.ExistsDirectory(ImportFromStablePath)) From fe11426238eaf2713414ddc63b0499df4c4b66ba Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 15:35:44 +0200 Subject: [PATCH 12/49] Disable appearance of the stable import prompt waiting for user interaction in tests, which caused them to fail. --- .../Navigation/TestScenePerformFromScreen.cs | 21 ++++++++++++------- osu.Game/Screens/Select/SongSelect.cs | 4 +++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 2791952b66..078bb817f8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -37,17 +37,17 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtSongSelect() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); - AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) })); + AddStep("perform immediately", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) })); AddAssert("did perform", () => actionPerformed); - AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect); } [Test] public void TestPerformAtMenuFromSongSelect() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); AddUntilStep("returned to menu", () => Game.ScreenStack.CurrentScreen is MainMenu); @@ -57,18 +57,18 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPerformAtSongSelectFromPlayerLoader() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); - AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(PlaySongSelect) })); - AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) })); + AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect); AddAssert("did perform", () => actionPerformed); } [Test] public void TestPerformAtMenuFromPlayerLoader() { - PushAndConfirm(() => new PlaySongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer())); AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true)); @@ -187,5 +187,10 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } + + public class TestPlaySongSelect : PlaySongSelect + { + protected override bool DisplayStableImportPrompt => false; + } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d8ad752151..5a48ee7606 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; + protected virtual bool DisplayStableImportPrompt => true; + /// /// Can be null if is false. /// @@ -282,7 +284,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable && DisplayStableImportPrompt) { dialogOverlay.Push(new ImportFromStablePopup(() => { From ed4c025c7e79669b198eb331e8d6fcc20ec04a9e Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 17:14:23 +0200 Subject: [PATCH 13/49] Fix other tests and move TestPlaySongSelect class declaration. --- .../Navigation/TestScenePerformFromScreen.cs | 7 +--- .../Navigation/TestSceneScreenNavigation.cs | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index 078bb817f8..3cedaf9d45 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -11,8 +11,8 @@ using osu.Game.Overlays; using osu.Game.Screens; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; -using osu.Game.Screens.Select; using osuTK.Input; +using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation; namespace osu.Game.Tests.Visual.Navigation { @@ -187,10 +187,5 @@ namespace osu.Game.Tests.Visual.Navigation return base.OnExiting(next); } } - - public class TestPlaySongSelect : PlaySongSelect - { - protected override bool DisplayStableImportPrompt => false; - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 859cefe3a9..253e448bb4 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -34,9 +34,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitSongSelectWithEscape() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); pushEscape(); @@ -51,9 +51,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestOpenModSelectOverlayUsingAction() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => InputManager.Key(Key.F1)); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); } @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation { Player player = null; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Navigation WorkingBeatmap beatmap() => Game.Beatmap.Value; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait()); @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Navigation WorkingBeatmap beatmap() => Game.Beatmap.Value; - PushAndConfirm(() => new TestSongSelect()); + PushAndConfirm(() => new TestPlaySongSelect()); AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait()); @@ -139,9 +139,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestMenuMakesMusic() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddUntilStep("wait for no track", () => Game.MusicController.CurrentTrack.IsDummyDevice); @@ -153,9 +153,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestExitSongSelectWithClick() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition)); @@ -213,9 +213,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestModSelectInput() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); @@ -234,9 +234,9 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestBeatmapOptionsInput() { - TestSongSelect songSelect = null; + TestPlaySongSelect songSelect = null; - PushAndConfirm(() => songSelect = new TestSongSelect()); + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); AddStep("Show options overlay", () => songSelect.BeatmapOptionsOverlay.Show()); @@ -312,11 +312,13 @@ namespace osu.Game.Tests.Visual.Navigation ConfirmAtMainMenu(); } - private class TestSongSelect : PlaySongSelect + public class TestPlaySongSelect : PlaySongSelect { public ModSelectOverlay ModSelectOverlay => ModSelect; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; + + protected override bool DisplayStableImportPrompt => false; } } } From db255e6814fa463c2a69893bde37cbcde0b58576 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sun, 16 May 2021 17:49:49 +0200 Subject: [PATCH 14/49] Mark StableImportManager as potentially null in tests. (StableImportManager is added to the DI in OsuGame and not in the OsuGameBase) --- .../Settings/Sections/Maintenance/GeneralSettings.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 9bd360679e..20fc0e962a 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -30,9 +30,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private TriangleButton undeleteButton; [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, StableImportManager stableImportManager, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importBeatmapsButton = new SettingsButton { @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importScoresButton = new SettingsButton { @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importSkinsButton = new SettingsButton { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (stableImportManager.SupportsImportFromStable) + if (stableImportManager?.SupportsImportFromStable ?? false) { Add(importCollectionsButton = new SettingsButton { From a38fc1a2e0bff4bcd36e34e926be4c98d3fcf7e3 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 13:04:49 +0200 Subject: [PATCH 15/49] Override text header. --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index f7c7934c63..4ea53a3fc1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -4,9 +4,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Localisation; using osu.Framework.Screens; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -16,11 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; - protected override OsuSpriteText CreateHeader() => new OsuSpriteText - { - Text = "Please select your osu!stable install location", - Font = OsuFont.Default.With(size: 40) - }; + public override LocalisableString HeaderText => "Please select your osu!stable install location"; public StableDirectorySelectScreen(TaskCompletionSource taskCompletionSource) { From 5ca4fd5ab4f53c61f88a4900b97c6c4150fb43b0 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 13:28:59 +0200 Subject: [PATCH 16/49] Block overlays to prevent getting into a bad state. --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 4ea53a3fc1..4aea05fb14 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private readonly TaskCompletionSource taskCompletionSource; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + protected override bool IsValidDirectory(DirectoryInfo info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; public override LocalisableString HeaderText => "Please select your osu!stable install location"; @@ -28,10 +30,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance this.Exit(); } - public override bool OnBackButton() + public override bool OnExiting(IScreen next) { taskCompletionSource.TrySetCanceled(); - return base.OnBackButton(); + return base.OnExiting(next); } } } From 6110a847aa82a2e0dd5c0bf90aa8c1db6bee1399 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 16:30:13 +0200 Subject: [PATCH 17/49] Simplify import ordering logic by making beatmapImportTask non-nullable. --- osu.Game/Database/StableImportManager.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 6f8225519d..67f91d3bdb 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -51,7 +51,7 @@ namespace osu.Game.Database var stableStorage = await getStableStorage().ConfigureAwait(false); var importTasks = new List(); - Task beatmapImportTask = default; + Task beatmapImportTask = Task.CompletedTask; if (content.HasFlagFast(StableContent.Beatmaps)) importTasks.Add(beatmapImportTask = beatmaps.ImportFromStableAsync(stableStorage)); @@ -59,16 +59,10 @@ namespace osu.Game.Database importTasks.Add(skins.ImportFromStableAsync(stableStorage)); if (content.HasFlagFast(StableContent.Collections)) - { - importTasks.Add(beatmapImportTask?.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) - ?? collections.ImportFromStableAsync(stableStorage)); - } + importTasks.Add(beatmapImportTask.ContinueWith(_ => collections.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); if (content.HasFlagFast(StableContent.Scores)) - { - importTasks.Add(beatmapImportTask?.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion) - ?? scores.ImportFromStableAsync(stableStorage)); - } + importTasks.Add(beatmapImportTask.ContinueWith(_ => scores.ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); } From 97952bc3f0fd105535999cd47a3f280640fa4c73 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 18:39:04 +0200 Subject: [PATCH 18/49] Fix backwards stable install resolution logic. --- osu.Game/Database/StableImportManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 67f91d3bdb..331764f274 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -69,13 +69,13 @@ namespace osu.Game.Database private async Task getStableStorage() { - var stableStorage = game.GetStorageForStableInstall(); - if (stableStorage != null) - return stableStorage; - if (cachedStorage != null) return cachedStorage; + var stableStorage = game.GetStorageForStableInstall(); + if (stableStorage != null) + return cachedStorage = stableStorage; + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); From e2018f81f3c2f59d46e9261b0518d01677277625 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 19:54:21 +0200 Subject: [PATCH 19/49] Use equality check for nullable types. --- .../Settings/Sections/Maintenance/GeneralSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 20fc0e962a..a38ca81e23 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] StableImportManager stableImportManager, DialogOverlay dialogOverlay) { - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importBeatmapsButton = new SettingsButton { @@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importScoresButton = new SettingsButton { @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } }); - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importSkinsButton = new SettingsButton { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance if (collectionManager != null) { - if (stableImportManager?.SupportsImportFromStable ?? false) + if (stableImportManager?.SupportsImportFromStable == true) { Add(importCollectionsButton = new SettingsButton { From 8530b31e39ca3eb5d6e275cc5f88ae9d5baf8efa Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 21:02:45 +0200 Subject: [PATCH 20/49] Use bitshifts for enum values instead of literal values. --- osu.Game/Database/StableImportManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 331764f274..63a6db35c0 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -87,10 +87,10 @@ namespace osu.Game.Database [Flags] public enum StableContent { - Beatmaps = 1, - Scores = 2, - Skins = 4, - Collections = 8, + Beatmaps = 1 << 0, + Scores = 1 << 1, + Skins = 1 << 2, + Collections = 1 << 3, All = Beatmaps | Scores | Skins | Collections } } From 79740dd2d8f70645becc7edf5134b3ee3a7041f2 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Mon, 17 May 2021 22:01:05 +0200 Subject: [PATCH 21/49] Merge conditionnal expression. --- osu.Game/Screens/Select/SongSelect.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5a48ee7606..729e25203f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; - protected virtual bool DisplayStableImportPrompt => true; + protected virtual bool DisplayStableImportPrompt => stableImportManager.SupportsImportFromStable; /// /// Can be null if is false. @@ -85,6 +85,9 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } + [Resolved] + private StableImportManager stableImportManager { get; set; } + protected ModSelectOverlay ModSelect { get; private set; } protected Sample SampleConfirm { get; private set; } @@ -102,7 +105,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, StableImportManager stableImportManager, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) + private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) { // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). transferRulesetValue(); @@ -284,7 +287,7 @@ namespace osu.Game.Screens.Select Schedule(() => { // if we have no beatmaps, let's prompt the user to import from over a stable install if he has one. - if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && stableImportManager.SupportsImportFromStable && DisplayStableImportPrompt) + if (!beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.Minimal).Any() && DisplayStableImportPrompt) { dialogOverlay.Push(new ImportFromStablePopup(() => { From c71d53a0f9ce174dc65640879df256a55397b1c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 May 2021 13:40:26 +0900 Subject: [PATCH 22/49] Fix text and button layout --- .../Maintenance/DirectorySelectScreen.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index e7c69e89fe..349a112477 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -11,9 +11,9 @@ using osuTK; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Framework.Screens; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -69,20 +69,24 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance RelativeSizeAxes = Axes.Both, RowDimensions = new[] { + new Dimension(GridSizeMode.AutoSize), new Dimension(), - new Dimension(GridSizeMode.Relative, 0.8f), - new Dimension(), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { new Drawable[] { - new OsuSpriteText + new OsuTextFlowContainer(cp => { - Text = HeaderText, - Font = OsuFont.Default.With(size: 40), - Origin = Anchor.Centre, - Anchor = Anchor.Centre, + cp.Font = OsuFont.Default.With(size: 24); + }) + { + Text = HeaderText.ToString(), + TextAnchor = Anchor.TopCentre, + Margin = new MarginPadding(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, } }, new Drawable[] @@ -99,6 +103,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 300, + Margin = new MarginPadding(10), Text = "Select directory", Action = () => OnSelection(directorySelector.CurrentPath.Value) }, From 775e0fbde5e6de0a7d20558e105b251ed994f8da Mon Sep 17 00:00:00 2001 From: Lucas A Date: Tue, 18 May 2021 15:27:20 +0200 Subject: [PATCH 23/49] Mark StableImportManager as nullable. --- osu.Game/Screens/Select/SongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 729e25203f..74e10037ab 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; - protected virtual bool DisplayStableImportPrompt => stableImportManager.SupportsImportFromStable; + protected virtual bool DisplayStableImportPrompt => stableImportManager?.SupportsImportFromStable == true; /// /// Can be null if is false. @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Select [Resolved] private BeatmapManager beatmaps { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private StableImportManager stableImportManager { get; set; } protected ModSelectOverlay ModSelect { get; private set; } From 76a377f3e09d88bde52e9309558633219f118e3f Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 15:30:45 +0200 Subject: [PATCH 24/49] Fixed applause sound stopping after switching scores --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 10 ---------- osu.Game/Screens/Ranking/ResultsScreen.cs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index bca3a07fa6..28829c4ed8 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -83,8 +83,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private Container badges; private RankText rankText; - private SkinnableSound applauseSound; - public AccuracyCircle(ScoreInfo score, bool withFlair) { this.score = score; @@ -211,13 +209,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy }, rankText = new RankText(score.Rank) }; - - if (withFlair) - { - AddInternal(applauseSound = score.Rank >= ScoreRank.A - ? new SkinnableSound(new SampleInfo("Results/rankpass", "applause")) - : new SkinnableSound(new SampleInfo("Results/rankfail"))); - } } private ScoreRank getRank(ScoreRank rank) @@ -256,7 +247,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy using (BeginDelayedSequence(TEXT_APPEAR_DELAY, true)) { - this.Delay(-1440).Schedule(() => applauseSound?.Play()); rankText.Appear(); } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index c1f5d92d17..20480c5367 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; @@ -19,7 +20,9 @@ using osu.Game.Online.API; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Ranking @@ -56,6 +59,8 @@ namespace osu.Game.Screens.Ranking private readonly bool allowRetry; private readonly bool allowWatchingReplay; + private SkinnableSound applauseSound; + protected ResultsScreen(ScoreInfo score, bool allowRetry, bool allowWatchingReplay = true) { Score = score; @@ -146,6 +151,13 @@ namespace osu.Game.Screens.Ranking bool shouldFlair = player != null && !Score.Mods.Any(m => m is ModAutoplay); ScorePanelList.AddScore(Score, shouldFlair); + + if (shouldFlair) + { + AddInternal(applauseSound = Score.Rank >= ScoreRank.A + ? new SkinnableSound(new SampleInfo("Results/rankpass", "applause")) + : new SkinnableSound(new SampleInfo("Results/rankfail"))); + } } if (allowWatchingReplay) @@ -183,6 +195,11 @@ namespace osu.Game.Screens.Ranking api.Queue(req); statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); + + using(BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) + { + this.Delay(-1000).Schedule(() => applauseSound?.Play()); + } } protected override void Update() From 06fffc499b3dd28759e44af39a5ca66176fbd193 Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 16:56:07 +0200 Subject: [PATCH 25/49] Removed unused variables and directives --- osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs | 5 ----- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 28829c4ed8..ec48a6313b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -10,11 +10,9 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; -using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Ranking.Expanded.Accuracy @@ -76,8 +74,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private readonly ScoreInfo score; - private readonly bool withFlair; - private SmoothCircularProgress accuracyCircle; private SmoothCircularProgress innerMask; private Container badges; @@ -86,7 +82,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy public AccuracyCircle(ScoreInfo score, bool withFlair) { this.score = score; - this.withFlair = withFlair; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 20480c5367..ab065f7dd4 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -196,7 +196,7 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); - using(BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) + using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) { this.Delay(-1000).Schedule(() => applauseSound?.Play()); } From ac5fe0c18cf50c2fa8b1bd112d0f9499b0516f18 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 18 May 2021 15:58:18 +0200 Subject: [PATCH 26/49] Change larger freemod selection overlay --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 375aac729d..fa30826ee9 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Origin = Anchor.BottomLeft, Depth = float.MinValue, RelativeSizeAxes = Axes.Both, - Height = 0.5f, + Height = 1.0f, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay { From d05ffdf120c0f13080f43d71b16884efb9d5da8c Mon Sep 17 00:00:00 2001 From: Vinicius Barbosa Date: Tue, 18 May 2021 20:19:18 +0200 Subject: [PATCH 27/49] Added constants for delay value --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 2 +- .../Expanded/Accuracy/AccuracyCircle.cs | 7 ++++++- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- osu.Game/Screens/Ranking/ScorePanel.cs | 18 +++++++++--------- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 2af15923a0..1e87893f39 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Ranking } } }, - new AccuracyCircle(score, true) + new AccuracyCircle(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index ec48a6313b..4fca4759f2 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -37,6 +37,11 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public const double ACCURACY_TRANSFORM_DURATION = 3000; + /// + /// Delay before the default applause sound is played to match the timing + /// + public const double APPLAUSE_DELAY = 1440; + /// /// Delay after for the rank text (A/B/C/D/S/SS) to appear. /// @@ -79,7 +84,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private Container badges; private RankText rankText; - public AccuracyCircle(ScoreInfo score, bool withFlair) + public AccuracyCircle(ScoreInfo score) { this.score = score; } diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 6a6b39b61c..4895240314 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Ranking.Expanded Margin = new MarginPadding { Top = 40 }, RelativeSizeAxes = Axes.X, Height = 230, - Child = new AccuracyCircle(score, withFlair) + Child = new AccuracyCircle(score) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index ab065f7dd4..e25bbbe253 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -198,7 +198,7 @@ namespace osu.Game.Screens.Ranking using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) { - this.Delay(-1000).Schedule(() => applauseSound?.Play()); + this.Delay(ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - AccuracyCircle.APPLAUSE_DELAY).Schedule(() => applauseSound?.Play()); } } diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index df710e4eb8..f66a998db6 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -54,12 +54,12 @@ namespace osu.Game.Screens.Ranking /// /// Duration for the panel to resize into its expanded/contracted size. /// - private const double resize_duration = 200; + public const double RESIZE_DURATION = 200; /// - /// Delay after before the top layer is expanded. + /// Delay after before the top layer is expanded. /// - private const double top_layer_expand_delay = 100; + public const double TOP_LAYER_EXPAND_DELAY = 100; /// /// Duration for the top layer expansion. @@ -208,8 +208,8 @@ namespace osu.Game.Screens.Ranking case PanelState.Expanded: Size = new Vector2(EXPANDED_WIDTH, expanded_height); - topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); - middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); + topLayerBackground.FadeColour(expanded_top_layer_colour, RESIZE_DURATION, Easing.OutQuint); + middleLayerBackground.FadeColour(expanded_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint); topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score, displayWithFlair).With(d => d.Alpha = 0)); @@ -221,20 +221,20 @@ namespace osu.Game.Screens.Ranking case PanelState.Contracted: Size = new Vector2(CONTRACTED_WIDTH, contracted_height); - topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); - middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); + topLayerBackground.FadeColour(contracted_top_layer_colour, RESIZE_DURATION, Easing.OutQuint); + middleLayerBackground.FadeColour(contracted_middle_layer_colour, RESIZE_DURATION, Easing.OutQuint); topLayerContentContainer.Add(topLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); middleLayerContentContainer.Add(middleLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } - content.ResizeTo(Size, resize_duration, Easing.OutQuint); + content.ResizeTo(Size, RESIZE_DURATION, Easing.OutQuint); bool topLayerExpanded = topLayerContainer.Y < 0; // If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state. - using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true)) + using (BeginDelayedSequence(topLayerExpanded ? 0 : RESIZE_DURATION + TOP_LAYER_EXPAND_DELAY, true)) { topLayerContainer.FadeIn(); From 1fd00d1313bd7ff8c5acf828843170b5f287b283 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 18 May 2021 21:52:28 +0200 Subject: [PATCH 28/49] Change from fullscreen to 0.7 --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index fa30826ee9..7395b346a4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Origin = Anchor.BottomLeft, Depth = float.MinValue, RelativeSizeAxes = Axes.Both, - Height = 1.0f, + Height = 0.7f, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay { From b2c736b42a52c91f8780ad00602a0dd18d1e6b83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 May 2021 18:09:46 +0900 Subject: [PATCH 29/49] Combine and move `const` closer to usage --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 5 ----- osu.Game/Screens/Ranking/ResultsScreen.cs | 11 +++++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 4fca4759f2..c70b4dd35b 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -37,11 +37,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public const double ACCURACY_TRANSFORM_DURATION = 3000; - /// - /// Delay before the default applause sound is played to match the timing - /// - public const double APPLAUSE_DELAY = 1440; - /// /// Delay after for the rank text (A/B/C/D/S/SS) to appear. /// diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e25bbbe253..a0ea27b640 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -29,6 +29,11 @@ namespace osu.Game.Screens.Ranking { public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler { + /// + /// Delay before the default applause sound should be played, in order to match the grade display timing in . + /// + public const double APPLAUSE_DELAY = AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY + ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - 1440; + protected const float BACKGROUND_BLUR = 20; private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y; @@ -196,10 +201,8 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); - using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY, true)) - { - this.Delay(ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - AccuracyCircle.APPLAUSE_DELAY).Schedule(() => applauseSound?.Play()); - } + using (BeginDelayedSequence(APPLAUSE_DELAY)) + Schedule(() => applauseSound?.Play()); } protected override void Update() From 8f5b28d26452211a913f7e2a2b67aece638367a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 13:51:08 +0900 Subject: [PATCH 30/49] Fix "folder missing" message showing incorrectly for beatmaps folder --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..dcbfbf1332 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps protected override string[] HashableFileTypes => new[] { ".osu" }; - protected override string ImportFromStablePath => "."; + protected override string ImportFromStablePath => string.Empty; protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 550daf36b5..8efd451857 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -691,10 +691,12 @@ namespace osu.Game.Database { var storage = PrepareStableStorage(stableStorage); + // Handle situations like when the user does not have a Skins folder. if (!storage.ExistsDirectory(ImportFromStablePath)) { - // This handles situations like when the user does not have a Skins folder - Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); + string fullPath = storage.GetFullPath(ImportFromStablePath); + + Logger.Log($"Folder \"{fullPath}\" not available in the target osu!stable installation to import {HumanisedModelName}s.", LoggingTarget.Information, LogLevel.Error); return Task.CompletedTask; } From 0489ae719de8109fdf1dc3cc26ffe65ccf609b7e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 14:53:33 +0900 Subject: [PATCH 31/49] Don't couple `PoolableDrawableWithLifetime` lifetime with its entry It turns out the incompatibility with `LifetimeManagementContainer` causes more issues than anticipated. --- .../Pooling/PoolableDrawableWithLifetime.cs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs index ed0430012a..64e1ac16bd 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PoolableDrawableWithLifetime.cs @@ -3,7 +3,6 @@ #nullable enable -using System; using System.Diagnostics; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Pooling; @@ -27,13 +26,14 @@ namespace osu.Game.Rulesets.Objects.Pooling /// protected bool HasEntryApplied { get; private set; } + // Drawable's lifetime gets out of sync with entry's lifetime if entry's lifetime is modified. + // We cannot delegate getter to `Entry.LifetimeStart` because it is incompatible with `LifetimeManagementContainer` due to how lifetime change is detected. public override double LifetimeStart { - get => Entry?.LifetimeStart ?? double.MinValue; + get => base.LifetimeStart; set { - if (Entry == null && LifetimeStart != value) - throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + base.LifetimeStart = value; if (Entry != null) Entry.LifetimeStart = value; @@ -42,11 +42,10 @@ namespace osu.Game.Rulesets.Objects.Pooling public override double LifetimeEnd { - get => Entry?.LifetimeEnd ?? double.MaxValue; + get => base.LifetimeEnd; set { - if (Entry == null && LifetimeEnd != value) - throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime)} when entry is not set"); + base.LifetimeEnd = value; if (Entry != null) Entry.LifetimeEnd = value; @@ -80,7 +79,12 @@ namespace osu.Game.Rulesets.Objects.Pooling free(); Entry = entry; + + base.LifetimeStart = entry.LifetimeStart; + base.LifetimeEnd = entry.LifetimeEnd; + OnApply(entry); + HasEntryApplied = true; } @@ -112,7 +116,11 @@ namespace osu.Game.Rulesets.Objects.Pooling Debug.Assert(Entry != null && HasEntryApplied); OnFree(Entry); + Entry = null; + base.LifetimeStart = double.MinValue; + base.LifetimeEnd = double.MaxValue; + HasEntryApplied = false; } } From abf96db54535e8e24a818db00ee6d19e921217ce Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 15:27:08 +0900 Subject: [PATCH 32/49] Add regression test for the pattern of using DHO proxy in `LifetimeManagementContainer` --- .../Gameplay/TestSceneProxyContainer.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs new file mode 100644 index 0000000000..9975c65085 --- /dev/null +++ b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Gameplay +{ + [HeadlessTest] + public class TestSceneProxyContainer : OsuTestScene + { + private HitObjectContainer hitObjectContainer; + private ProxyContainer proxyContainer; + private readonly ManualClock clock = new ManualClock(); + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = new Container + { + Children = new Drawable[] + { + hitObjectContainer = new HitObjectContainer(), + proxyContainer = new ProxyContainer() + }, + Clock = new FramedClock(clock) + }; + clock.CurrentTime = 0; + }); + + [Test] + public void TestProxyLifetimeManagement() + { + AddStep("Add proxy drawables", () => + { + addProxy(new TestDrawableHitObject(1000)); + addProxy(new TestDrawableHitObject(3000)); + addProxy(new TestDrawableHitObject(5000)); + }); + + AddStep($"time = 1000", () => clock.CurrentTime = 1000); + AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); + AddStep($"time = 5000", () => clock.CurrentTime = 5000); + AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); + AddStep($"time = 6000", () => clock.CurrentTime = 6000); + AddAssert("No proxy is alive", () => proxyContainer.AliveChildren.Count == 0); + } + + private void addProxy(DrawableHitObject drawableHitObject) + { + hitObjectContainer.Add(drawableHitObject); + proxyContainer.AddProxy(drawableHitObject); + } + + private class ProxyContainer : LifetimeManagementContainer + { + public IReadOnlyList AliveChildren => AliveInternalChildren; + + public void AddProxy(Drawable d) => AddInternal(d.CreateProxy()); + } + + private class TestDrawableHitObject : DrawableHitObject + { + protected override double InitialLifetimeOffset => 100; + + public TestDrawableHitObject(double startTime) + : base(new HitObject { StartTime = startTime }) + { + } + + protected override void UpdateInitialTransforms() + { + LifetimeEnd = LifetimeStart + 500; + } + } + } +} From 3018a41ab5daf24a61671cc1684cc8f089049e8a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 20 May 2021 16:00:49 +0900 Subject: [PATCH 33/49] Remove redundant string interpolation --- osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs index 9975c65085..1264d575a4 100644 --- a/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs +++ b/osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs @@ -46,11 +46,11 @@ namespace osu.Game.Tests.Gameplay addProxy(new TestDrawableHitObject(5000)); }); - AddStep($"time = 1000", () => clock.CurrentTime = 1000); + AddStep("time = 1000", () => clock.CurrentTime = 1000); AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); - AddStep($"time = 5000", () => clock.CurrentTime = 5000); + AddStep("time = 5000", () => clock.CurrentTime = 5000); AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1); - AddStep($"time = 6000", () => clock.CurrentTime = 6000); + AddStep("time = 6000", () => clock.CurrentTime = 6000); AddAssert("No proxy is alive", () => proxyContainer.AliveChildren.Count == 0); } From 89b4f695884ca229193dc8e7bd2b5d009847a55b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:39 +0900 Subject: [PATCH 34/49] Expose playing user states as bindable dictionary --- osu.Game/Online/Spectator/SpectatorClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index f930328846..810299e90d 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -39,10 +39,10 @@ namespace osu.Game.Online.Spectator private readonly List watchingUsers = new List(); public IBindableList PlayingUsers => playingUsers; - private readonly BindableList playingUsers = new BindableList(); - private readonly Dictionary playingUserStates = new Dictionary(); + public IBindableDictionary PlayingUserStates => playingUserStates; + private readonly BindableDictionary playingUserStates = new BindableDictionary(); private IBeatmap? currentBeatmap; From b515fe3cb1f9ee8732bd427dfc842cbefca3a9b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:19:58 +0900 Subject: [PATCH 35/49] Fix playing user state not removed on stop watching --- osu.Game/Online/Spectator/SpectatorClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 810299e90d..649af03bc4 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -183,6 +183,7 @@ namespace osu.Game.Online.Spectator public void StopWatchingUser(int userId) { watchingUsers.Remove(userId); + playingUserStates.Remove(userId); StopWatchingUserInternal(userId); } From 7ee81669f7a304982bb88cccf5cf354a9cc72131 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:27:34 +0900 Subject: [PATCH 36/49] Remove bind helpers from SpectatorClient --- osu.Game/Online/Spectator/SpectatorClient.cs | 28 -------- osu.Game/Screens/Spectate/SpectatorScreen.cs | 68 +++++++++++--------- 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 649af03bc4..04904f66ac 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -238,33 +238,5 @@ namespace osu.Game.Online.Spectator lastSendTime = Time.Current; } - - /// - /// Attempts to retrieve the for a currently-playing user. - /// - /// The user. - /// The current for the user, if they're playing. null if the user is not playing. - /// true if successful (the user is playing), false otherwise. - public bool TryGetPlayingUserState(int userId, out SpectatorState state) - { - return playingUserStates.TryGetValue(userId, out state); - } - - /// - /// Bind an action to with the option of running the bound action once immediately. - /// - /// The action to perform when a user begins playing. - /// Whether the action provided in should be run once immediately for all users currently playing. - public void BindUserBeganPlaying(Action callback, bool runOnceImmediately = false) - { - // The lock is taken before the event is subscribed to to prevent doubling of events. - OnUserBeganPlaying += callback; - - if (!runOnceImmediately) - return; - - foreach (var (userId, state) in playingUserStates) - callback(userId, state); - } } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index e6c9a0acd4..0adc5b863f 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.Spectator; @@ -42,6 +43,8 @@ namespace osu.Game.Screens.Spectate [Resolved] private UserLookupCache userLookupCache { get; set; } + private readonly IBindableDictionary playingUserStates = new BindableDictionary(); + private readonly Dictionary userMap = new Dictionary(); private readonly Dictionary gameplayStates = new Dictionary(); @@ -65,8 +68,9 @@ namespace osu.Game.Screens.Spectate foreach (var u in users.Result) userMap[u.Id] = u; - spectatorClient.BindUserBeganPlaying(userBeganPlaying, true); - spectatorClient.OnUserFinishedPlaying += userFinishedPlaying; + playingUserStates.BindTo(spectatorClient.PlayingUserStates); + playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); + spectatorClient.OnNewFrames += userSentFrames; managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); @@ -102,7 +106,7 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in userMap) { - if (!spectatorClient.TryGetPlayingUserState(userId, out var userState)) + if (!playingUserStates.TryGetValue(userId, out var userState)) continue; if (beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == userState.BeatmapID)) @@ -110,7 +114,23 @@ namespace osu.Game.Screens.Spectate } } - private void userBeganPlaying(int userId, SpectatorState state) + private void onPlayingUserStatesChanged(object sender, NotifyDictionaryChangedEventArgs e) + { + switch (e.Action) + { + case NotifyDictionaryChangedAction.Add: + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; + + case NotifyDictionaryChangedAction.Remove: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + break; + } + } + + private void onUserStateAdded(int userId, SpectatorState state) { if (state.RulesetID == null || state.BeatmapID == null) return; @@ -118,24 +138,30 @@ namespace osu.Game.Screens.Spectate if (!userMap.ContainsKey(userId)) return; - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out _)) + Schedule(() => OnUserStateChanged(userId, state)); + updateGameplayState(userId); + } + + private void onUserStateRemoved(int userId) + { + if (!userMap.ContainsKey(userId)) return; - Schedule(() => OnUserStateChanged(userId, state)); + if (!gameplayStates.TryGetValue(userId, out var gameplayState)) + return; - updateGameplayState(userId); + gameplayState.Score.Replay.HasReceivedAllFrames = true; + + gameplayStates.Remove(userId); + Schedule(() => EndGameplay(userId)); } private void updateGameplayState(int userId) { Debug.Assert(userMap.ContainsKey(userId)); - // The user may have stopped playing. - if (!spectatorClient.TryGetPlayingUserState(userId, out var spectatorState)) - return; - var user = userMap[userId]; + var spectatorState = playingUserStates[userId]; var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == spectatorState.RulesetID)?.CreateInstance(); if (resolvedRuleset == null) @@ -186,20 +212,6 @@ namespace osu.Game.Screens.Spectate } } - private void userFinishedPlaying(int userId, SpectatorState state) - { - if (!userMap.ContainsKey(userId)) - return; - - if (!gameplayStates.TryGetValue(userId, out var gameplayState)) - return; - - gameplayState.Score.Replay.HasReceivedAllFrames = true; - - gameplayStates.Remove(userId); - Schedule(() => EndGameplay(userId)); - } - /// /// Invoked when a spectated user's state has changed. /// @@ -226,7 +238,7 @@ namespace osu.Game.Screens.Spectate /// The user to stop spectating. protected void RemoveUser(int userId) { - userFinishedPlaying(userId, null); + onUserStateRemoved(userId); userIds.Remove(userId); userMap.Remove(userId); @@ -240,8 +252,6 @@ namespace osu.Game.Screens.Spectate if (spectatorClient != null) { - spectatorClient.OnUserBeganPlaying -= userBeganPlaying; - spectatorClient.OnUserFinishedPlaying -= userFinishedPlaying; spectatorClient.OnNewFrames -= userSentFrames; foreach (var (userId, _) in userMap) From ee4bca9ed12d5f65cc55d3b24f1164851c89c746 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 May 2021 19:37:43 +0900 Subject: [PATCH 37/49] Handle collection changed event --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 0adc5b863f..9a20bb58b8 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -127,6 +127,14 @@ namespace osu.Game.Screens.Spectate foreach (var (userId, _) in e.OldItems.AsNonNull()) onUserStateRemoved(userId); break; + + case NotifyDictionaryChangedAction.Replace: + foreach (var (userId, _) in e.OldItems.AsNonNull()) + onUserStateRemoved(userId); + + foreach (var (userId, state) in e.NewItems.AsNonNull()) + onUserStateAdded(userId, state); + break; } } From c00e6e29a6f2c32da0ae882f98344afb94770812 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 14:21:56 +0900 Subject: [PATCH 38/49] Remove `static` usage --- .../Edit/Blueprints/HitPlacementBlueprint.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs index 17e7fb81f6..0d0fd136a7 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/HitPlacementBlueprint.cs @@ -14,10 +14,10 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints { private readonly HitPiece piece; - private static Hit hit; + public new Hit HitObject => (Hit)base.HitObject; public HitPlacementBlueprint() - : base(hit = new Hit()) + : base(new Hit()) { InternalChild = piece = new HitPiece { @@ -30,12 +30,12 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints switch (e.Button) { case MouseButton.Left: - hit.Type = HitType.Centre; + HitObject.Type = HitType.Centre; EndPlacement(true); return true; case MouseButton.Right: - hit.Type = HitType.Rim; + HitObject.Type = HitType.Rim; EndPlacement(true); return true; } From 40c8378d81a67b944c63d54a86b413ab70af7cb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 14:37:22 +0900 Subject: [PATCH 39/49] Fix type-to-sample mapping being applied too late --- .../Objects/Drawables/DrawableHit.cs | 28 +---------------- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 38cda69a46..948d2c2f69 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -55,16 +55,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables type.BindValueChanged(_ => { updateActionsFromType(); - - // will overwrite samples, should only be called on subsequent changes - // after the initial application. - updateSamplesFromTypeChange(); - RecreatePieces(); - }); - - // action update also has to happen immediately on application. - updateActionsFromType(); + }, true); base.OnApply(); } @@ -92,24 +84,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables type.Value = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } - private void updateSamplesFromTypeChange() - { - var rimSamples = getRimSamples(); - - bool isRimType = HitObject.Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - HitObject.Samples.Remove(sample); - } - } - } - private void updateActionsFromType() { HitActions = diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 1b51288605..f4a66c39a8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.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.Linq; using osu.Framework.Bindables; +using osu.Game.Audio; namespace osu.Game.Rulesets.Taiko.Objects { @@ -15,9 +17,36 @@ namespace osu.Game.Rulesets.Taiko.Objects public HitType Type { get => TypeBindable.Value; - set => TypeBindable.Value = value; + set + { + TypeBindable.Value = value; + updateSamplesFromType(); + } } + private void updateSamplesFromType() + { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } + } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject From f9d51656b6dff672eab55e46eb1bad287d70b84e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 15:02:36 +0900 Subject: [PATCH 40/49] Fix scaling of rotated items not behaving in an understandable way --- .../Skinning/Editor/SkinSelectionHandler.cs | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2f9611ba65..accb65483b 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Utils; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; @@ -37,40 +38,44 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + // the selection quad is always upright, so use an AABB rect to make mutating the values easier. + var selectionRect = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => + b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())).AABBFloat; - // the selection quad is always upright, so use a rect to make mutating the values easier. - var adjustedRect = selectionQuad.AABBFloat; + // copy to mutate, as we will need to compare to the original later on. + var adjustedRect = selectionRect; - // for now aspect lock scale adjustments that occur at corners. - if (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) - scale.Y = scale.X / selectionQuad.Width * selectionQuad.Height; + // first, remove any scale axis we are not interested in. + if (anchor.HasFlagFast(Anchor.x1)) scale.X = 0; + if (anchor.HasFlagFast(Anchor.y1)) scale.Y = 0; - if (anchor.HasFlagFast(Anchor.x0)) + bool shouldAspectLock = + // for now aspect lock scale adjustments that occur at corners.. + (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) + // ..or if any of the selection have been rotated. + // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). + || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation % 90, 0)); + + if (shouldAspectLock) { - adjustedRect.X -= scale.X; - adjustedRect.Width += scale.X; - } - else if (anchor.HasFlagFast(Anchor.x2)) - { - adjustedRect.Width += scale.X; + if (anchor.HasFlagFast(Anchor.x1)) + // if dragging from the horizontal centre, only a vertical component is available. + scale.X = scale.Y / selectionRect.Height * selectionRect.Width; + else + // in all other cases (arbitrarily) use the horizontal component for aspect lock. + scale.Y = scale.X / selectionRect.Width * selectionRect.Height; } - if (anchor.HasFlagFast(Anchor.y0)) - { - adjustedRect.Y -= scale.Y; - adjustedRect.Height += scale.Y; - } - else if (anchor.HasFlagFast(Anchor.y2)) - { - adjustedRect.Height += scale.Y; - } + if (anchor.HasFlagFast(Anchor.x0)) adjustedRect.X -= scale.X; + if (anchor.HasFlagFast(Anchor.y0)) adjustedRect.Y -= scale.Y; - // scale adjust should match that of the quad itself. + adjustedRect.Width += scale.X; + adjustedRect.Height += scale.Y; + + // scale adjust applied to each individual item should match that of the quad itself. var scaledDelta = new Vector2( - adjustedRect.Width / selectionQuad.Width, - adjustedRect.Height / selectionQuad.Height + adjustedRect.Width / selectionRect.Width, + adjustedRect.Height / selectionRect.Height ); foreach (var b in SelectedBlueprints) @@ -82,8 +87,8 @@ namespace osu.Game.Skinning.Editor var relativePositionInOriginal = new Vector2( - (screenPosition.X - selectionQuad.TopLeft.X) / selectionQuad.Width, - (screenPosition.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height + (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, + (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height ); var newPositionInAdjusted = new Vector2( From 0d575f572866e84421829b7002ef0b695ad4e59f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 15:06:53 +0900 Subject: [PATCH 41/49] Remove incorrect (and unintended) modulus logic --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index accb65483b..0b6d9222ce 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -54,7 +54,7 @@ namespace osu.Game.Skinning.Editor (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) // ..or if any of the selection have been rotated. // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). - || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation % 90, 0)); + || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation, 0)); if (shouldAspectLock) { From a5ca736e3729d7a57f414834634531d4c8372c28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:10:48 +0900 Subject: [PATCH 42/49] Fix `RecreatePieces` being called more than once --- .../Objects/Drawables/DrawableHit.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 948d2c2f69..f2b1284a95 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -52,15 +52,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void OnApply() { type.BindTo(HitObject.TypeBindable); - type.BindValueChanged(_ => - { - updateActionsFromType(); - RecreatePieces(); - }, true); + // this doesn't need to be run inline as RecreatePieces is called by the base call below. + type.BindValueChanged(_ => RecreatePieces()); base.OnApply(); } + protected override void RecreatePieces() + { + updateActionsFromType(); + base.RecreatePieces(); + } + protected override void OnFree() { base.OnFree(); From 7bc8a4bb5fa9e65937340f06560d5ab27ad5ac80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:15:27 +0900 Subject: [PATCH 43/49] Apply same logic changes to `IsStrong` status --- .../Objects/Drawables/DrawableHit.cs | 9 ----- .../DrawableTaikoStrongableHitObject.cs | 36 ++----------------- .../Objects/TaikoStrongableHitObject.cs | 26 +++++++++++++- 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index f2b1284a95..5cb2024bb7 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -78,15 +78,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = pressHandledThisFrame = false; } - private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); - - protected override void LoadSamples() - { - base.LoadSamples(); - - type.Value = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - private void updateActionsFromType() { HitActions = diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index 4f1523eb3f..b4acfa9968 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Audio; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -29,14 +27,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override void OnApply() { isStrong.BindTo(HitObject.IsStrongBindable); - isStrong.BindValueChanged(_ => - { - // will overwrite samples, should only be called on subsequent changes - // after the initial application. - updateSamplesFromStrong(); - - RecreatePieces(); - }); + // this doesn't need to be run inline as RecreatePieces is called by the base call below. + isStrong.BindValueChanged(_ => RecreatePieces()); base.OnApply(); } @@ -50,30 +42,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables isStrong.UnbindEvents(); } - private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); - - protected override void LoadSamples() - { - base.LoadSamples(); - isStrong.Value = getStrongSamples().Any(); - } - - private void updateSamplesFromStrong() - { - var strongSamples = getStrongSamples(); - - if (isStrong.Value != strongSamples.Any()) - { - if (isStrong.Value) - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); - else - { - foreach (var sample in strongSamples) - HitObject.Samples.Remove(sample); - } - } - } - protected override void RecreatePieces() { base.RecreatePieces(); diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index fcd055bcec..cac56d1269 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using System.Threading; using osu.Framework.Bindables; +using osu.Game.Audio; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Taiko.Objects @@ -31,9 +33,31 @@ namespace osu.Game.Rulesets.Taiko.Objects public bool IsStrong { get => IsStrongBindable.Value; - set => IsStrongBindable.Value = value; + set + { + IsStrongBindable.Value = value; + updateSamplesFromStrong(); + } } + private void updateSamplesFromStrong() + { + var strongSamples = getStrongSamples(); + + if (IsStrongBindable.Value != strongSamples.Any()) + { + if (IsStrongBindable.Value) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); + else + { + foreach (var sample in strongSamples) + Samples.Remove(sample); + } + } + } + + private HitSampleInfo[] getStrongSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray(); + protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { base.CreateNestedHitObjects(cancellationToken); From 0bcd0cda6b1ed7facd0264fae35b133bff7f2f5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:41:39 +0900 Subject: [PATCH 44/49] Fix taiko drawable hit content not correctly being removed on regeneration --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 6041eccb51..6a8d8a611c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -137,7 +137,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { Size = BaseSize = new Vector2(TaikoHitObject.DEFAULT_SIZE); - MainPiece?.Expire(); + if (MainPiece != null) + Content.Remove(MainPiece); + Content.Add(MainPiece = CreateMainPiece()); } From 6471ce902d44fce94676f7a8a73ee4e960a07d40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 16:45:28 +0900 Subject: [PATCH 45/49] Run `RecreatePieces` using `AddOnce` to avoid multiple unnecessary calls --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- .../Objects/Drawables/DrawableTaikoStrongableHitObject.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 5cb2024bb7..1e9fc187eb 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { type.BindTo(HitObject.TypeBindable); // this doesn't need to be run inline as RecreatePieces is called by the base call below. - type.BindValueChanged(_ => RecreatePieces()); + type.BindValueChanged(_ => Scheduler.AddOnce(RecreatePieces)); base.OnApply(); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs index b4acfa9968..70d4371e99 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoStrongableHitObject.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { isStrong.BindTo(HitObject.IsStrongBindable); // this doesn't need to be run inline as RecreatePieces is called by the base call below. - isStrong.BindValueChanged(_ => RecreatePieces()); + isStrong.BindValueChanged(_ => Scheduler.AddOnce(RecreatePieces)); base.OnApply(); } From 0c504c3b7d0adad741bf4d6e0fe2f25538644e8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:24:22 +0900 Subject: [PATCH 46/49] 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 90d131b117..57550cfb93 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 587bdaf622..1e3b77cd70 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 7ba7a554d6..a2a9ac35fc 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 8085a5420580065222be24b465fefcd26b1f60ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:28:25 +0900 Subject: [PATCH 47/49] Add test coverage of different grade types to `TestSceneResultsScreen` --- .../Visual/Ranking/TestSceneAccuracyCircle.cs | 91 +++---------------- .../Visual/Ranking/TestSceneResultsScreen.cs | 36 +++++--- 2 files changed, 35 insertions(+), 92 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 1e87893f39..f305b7255e 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -22,82 +22,17 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneAccuracyCircle : OsuTestScene { - [Test] - public void TestLowDRank() + [TestCase(0.2, ScoreRank.D)] + [TestCase(0.5, ScoreRank.D)] + [TestCase(0.75, ScoreRank.C)] + [TestCase(0.85, ScoreRank.B)] + [TestCase(0.925, ScoreRank.A)] + [TestCase(0.975, ScoreRank.S)] + [TestCase(0.9999, ScoreRank.S)] + [TestCase(1, ScoreRank.X)] + public void TestRank(double accuracy, ScoreRank rank) { - var score = createScore(); - score.Accuracy = 0.2; - score.Rank = ScoreRank.D; - - addCircleStep(score); - } - - [Test] - public void TestDRank() - { - var score = createScore(); - score.Accuracy = 0.5; - score.Rank = ScoreRank.D; - - addCircleStep(score); - } - - [Test] - public void TestCRank() - { - var score = createScore(); - score.Accuracy = 0.75; - score.Rank = ScoreRank.C; - - addCircleStep(score); - } - - [Test] - public void TestBRank() - { - var score = createScore(); - score.Accuracy = 0.85; - score.Rank = ScoreRank.B; - - addCircleStep(score); - } - - [Test] - public void TestARank() - { - var score = createScore(); - score.Accuracy = 0.925; - score.Rank = ScoreRank.A; - - addCircleStep(score); - } - - [Test] - public void TestSRank() - { - var score = createScore(); - score.Accuracy = 0.975; - score.Rank = ScoreRank.S; - - addCircleStep(score); - } - - [Test] - public void TestAlmostSSRank() - { - var score = createScore(); - score.Accuracy = 0.9999; - score.Rank = ScoreRank.S; - - addCircleStep(score); - } - - [Test] - public void TestSSRank() - { - var score = createScore(); - score.Accuracy = 1; - score.Rank = ScoreRank.X; + var score = createScore(accuracy, rank); addCircleStep(score); } @@ -129,7 +64,7 @@ namespace osu.Game.Tests.Visual.Ranking }; }); - private ScoreInfo createScore() => new ScoreInfo + private ScoreInfo createScore(double accuracy, ScoreRank rank) => new ScoreInfo { User = new User { @@ -139,9 +74,9 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, TotalScore = 2845370, - Accuracy = 0.95, + Accuracy = accuracy, MaxCombo = 999, - Rank = ScoreRank.S, + Rank = rank, Date = DateTimeOffset.Now, Statistics = { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index b2be7cdf88..ba6b6bd529 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -29,13 +29,8 @@ namespace osu.Game.Tests.Visual.Ranking [TestFixture] public class TestSceneResultsScreen : OsuManualInputManagerTestScene { - private BeatmapManager beatmaps; - - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps) - { - this.beatmaps = beatmaps; - } + [Resolved] + private BeatmapManager beatmaps { get; set; } protected override void LoadComplete() { @@ -46,10 +41,6 @@ namespace osu.Game.Tests.Visual.Ranking Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); } - private TestResultsScreen createResultsScreen() => new TestResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - - private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - [Test] public void TestResultsWithoutPlayer() { @@ -69,12 +60,25 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("retry overlay not present", () => screen.RetryOverlay == null); } - [Test] - public void TestResultsWithPlayer() + [TestCase(0.2, ScoreRank.D)] + [TestCase(0.5, ScoreRank.D)] + [TestCase(0.75, ScoreRank.C)] + [TestCase(0.85, ScoreRank.B)] + [TestCase(0.925, ScoreRank.A)] + [TestCase(0.975, ScoreRank.S)] + [TestCase(0.9999, ScoreRank.S)] + [TestCase(1, ScoreRank.X)] + public void TestResultsWithPlayer(double accuracy, ScoreRank rank) { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Accuracy = accuracy, + Rank = rank + }; + + AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen(score))); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -232,6 +236,10 @@ namespace osu.Game.Tests.Visual.Ranking AddAssert("download button is enabled", () => screen.ChildrenOfType().Last().Enabled.Value); } + private TestResultsScreen createResultsScreen(ScoreInfo score = null) => new TestResultsScreen(score ?? new TestScoreInfo(new OsuRuleset().RulesetInfo)); + + private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + private class TestResultsContainer : Container { [Cached(typeof(Player))] From 41c4afb3d592e05ff1ab62003736e3716c7be856 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:46:25 +0900 Subject: [PATCH 48/49] Restore path specification to `"."` for consistency --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index dcbfbf1332..5e975de77c 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Beatmaps protected override string[] HashableFileTypes => new[] { ".osu" }; - protected override string ImportFromStablePath => string.Empty; + protected override string ImportFromStablePath => "."; protected override Storage PrepareStableStorage(StableStorage stableStorage) => stableStorage.GetSongStorage(); From abc96057b2cf50e02e0ec939645f6421684495d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:55:46 +0900 Subject: [PATCH 49/49] Remove relative height specification and use constant height --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 ++- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 754b260bf0..e31e307d4d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -96,7 +96,8 @@ namespace osu.Game.Overlays.Mods Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); Waves.FourthWaveColour = Color4Extensions.FromHex(@"003a4e"); - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + Height = HEIGHT; Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 7395b346a4..a53e253581 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -72,8 +72,8 @@ namespace osu.Game.Screens.OnlinePlay.Match Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Depth = float.MinValue, - RelativeSizeAxes = Axes.Both, - Height = 0.7f, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }, Child = userModsSelectOverlay = new UserModSelectOverlay {