From 5d5b1e1f0e441de05c7c5fda27399d05a84415f9 Mon Sep 17 00:00:00 2001 From: Lucas A Date: Sat, 8 May 2021 11:00:22 +0200 Subject: [PATCH 01/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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 8f5b28d26452211a913f7e2a2b67aece638367a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 13:51:08 +0900 Subject: [PATCH 24/25] 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 41c4afb3d592e05ff1ab62003736e3716c7be856 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 May 2021 17:46:25 +0900 Subject: [PATCH 25/25] 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();