From 6621d363da05951e3208f785f472190802638789 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 17:01:05 +0900 Subject: [PATCH 1/8] Add basic custom data directory support --- .../NonVisual/CustomDataDirectoryTest.cs | 88 +++++++++++++++++++ .../Configuration/StorageConfigManager.cs | 30 +++++++ osu.Game/OsuGameBase.cs | 23 ++++- 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs create mode 100644 osu.Game/Configuration/StorageConfigManager.cs diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs new file mode 100644 index 0000000000..2d5f1f238f --- /dev/null +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -0,0 +1,88 @@ +// 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.IO; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class CustomDataDirectoryTest + { + [Test] + public void TestDefaultDirectory() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestDefaultDirectory))) + { + try + { + var osu = loadOsu(host); + var storage = osu.Dependencies.Get(); + + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless-{nameof(TestDefaultDirectory)}"); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestCustomDirectory() + { + using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) + { + string headlessPrefix = $"headless-{nameof(TestCustomDirectory)}"; + + // need access before the game has constructed its own storage yet. + Storage storage = new DesktopStorage(headlessPrefix, host); + // manual cleaning so we can prepare a config file. + storage.DeleteDirectory(string.Empty); + + using (var storageConfig = new StorageConfigManager(storage)) + storageConfig.Set(StorageConfig.FullPath, Path.Combine(Environment.CurrentDirectory, "custom-path")); + + try + { + var osu = loadOsu(host); + + // switch to DI'd storage + storage = osu.Dependencies.Get(); + + Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(Environment.CurrentDirectory, "custom-path"))); + } + finally + { + host.Exit(); + } + } + } + + private OsuGameBase loadOsu(GameHost host) + { + var osu = new OsuGameBase(); + Task.Run(() => host.Run(osu)); + waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time"); + return osu; + } + + private static void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) + { + Task task = Task.Run(() => + { + while (!result()) Thread.Sleep(200); + }); + + Assert.IsTrue(task.Wait(timeout), failureMessage); + } + } +} diff --git a/osu.Game/Configuration/StorageConfigManager.cs b/osu.Game/Configuration/StorageConfigManager.cs new file mode 100644 index 0000000000..929f8f22ad --- /dev/null +++ b/osu.Game/Configuration/StorageConfigManager.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Configuration; +using osu.Framework.Platform; + +namespace osu.Game.Configuration +{ + public class StorageConfigManager : IniConfigManager + { + protected override string Filename => "storage.ini"; + + public StorageConfigManager(Storage storage) + : base(storage) + { + } + + protected override void InitialiseDefaults() + { + base.InitialiseDefaults(); + + Set(StorageConfig.FullPath, string.Empty); + } + } + + public enum StorageConfig + { + FullPath, + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 609b6ce98e..fe25197294 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -132,6 +132,8 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); + dependencies.CacheAs(Storage); + var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); @@ -300,8 +302,13 @@ namespace osu.Game { base.SetHost(host); - if (Storage == null) - Storage = host.Storage; + var storageConfig = new StorageConfigManager(host.Storage); + + var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + + Storage = !string.IsNullOrEmpty(customStoragePath) + ? new CustomStorage(customStoragePath, host) + : host.Storage; if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); @@ -353,5 +360,17 @@ namespace osu.Game public override bool ChangeFocusOnClick => false; } } + + /// + /// A storage pointing to an absolute location specified by the user to store game data files. + /// + private class CustomStorage : NativeStorage + { + public CustomStorage(string fullPath, GameHost host) + : base(string.Empty, host) + { + BasePath = fullPath; + } + } } } From 5edabbdee25e38e3e0d1ab08296e5a4f6119e79b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 17:35:35 +0900 Subject: [PATCH 2/8] Redirect log output to custom data directory --- osu.Game/OsuGameBase.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fe25197294..f92db4e111 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,6 +71,8 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; + protected StorageConfigManager StorageConfig; + private Container content; protected override Container Content => content; @@ -302,13 +304,17 @@ namespace osu.Game { base.SetHost(host); - var storageConfig = new StorageConfigManager(host.Storage); + StorageConfig = new StorageConfigManager(host.Storage); - var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + var customStoragePath = StorageConfig.Get(Configuration.StorageConfig.FullPath); - Storage = !string.IsNullOrEmpty(customStoragePath) - ? new CustomStorage(customStoragePath, host) - : host.Storage; + if (!string.IsNullOrEmpty(customStoragePath)) + { + Storage = new CustomStorage(customStoragePath, host); + Logger.Storage = Storage.GetStorageForDirectory("logs"); + } + else + Storage = host.Storage; if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); From 7781408643a682358c463077bf76a6d35a4bc1c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 May 2020 18:27:10 +0900 Subject: [PATCH 3/8] Update in line with framework storage changes --- osu.Desktop/OsuGameDesktop.cs | 78 +++++++++++------------ osu.Game.Tournament/IPC/FileBasedIPC.cs | 84 +++++++++++-------------- osu.Game/IO/OsuStorage.cs | 26 ++++++++ osu.Game/IO/WrappedStorage.cs | 80 +++++++++++++++++++++++ osu.Game/OsuGameBase.cs | 26 +------- 5 files changed, 179 insertions(+), 115 deletions(-) create mode 100644 osu.Game/IO/OsuStorage.cs create mode 100644 osu.Game/IO/WrappedStorage.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f05ee48914..9351e17419 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -6,15 +6,14 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; using osuTK.Input; -using Microsoft.Win32; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; -using osu.Framework.Platform.Windows; using osu.Framework.Screens; using osu.Game.Screens.Menu; using osu.Game.Updater; @@ -37,7 +36,11 @@ namespace osu.Desktop try { if (Host is DesktopGameHost desktopHost) - return new StableStorage(desktopHost); + { + string stablePath = getStableInstallPath(); + if (!string.IsNullOrEmpty(stablePath)) + return new DesktopStorage(stablePath, desktopHost); + } } catch (Exception) { @@ -47,6 +50,35 @@ namespace osu.Desktop return null; } + private string getStableInstallPath() + { + static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); + + string stableInstallPath; + + try + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + protected override UpdateManager CreateUpdateManager() { switch (RuntimeInfo.OS) @@ -111,45 +143,5 @@ namespace osu.Desktop Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); } - - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage - { - protected override string LocateBasePath() - { - static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); - - string stableInstallPath; - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; - } - - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) - { - } - } } } diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index eefa9fcfe6..53ba597a7e 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -8,7 +8,6 @@ using Microsoft.Win32; using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Platform.Windows; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; @@ -52,7 +51,12 @@ namespace osu.Game.Tournament.IPC try { - Storage = new StableStorage(host as DesktopGameHost); + var path = findStablePath(); + + if (string.IsNullOrEmpty(path)) + return null; + + Storage = new DesktopStorage(path, host as DesktopGameHost); const string file_ipc_filename = "ipc.txt"; const string file_ipc_state_filename = "ipc-state.txt"; @@ -145,64 +149,50 @@ namespace osu.Game.Tournament.IPC return Storage; } - /// - /// A method of accessing an osu-stable install in a controlled fashion. - /// - private class StableStorage : WindowsStorage + private string findStablePath() { - protected override string LocateBasePath() - { - static bool checkExists(string p) - { - return File.Exists(Path.Combine(p, "ipc.txt")); - } + static bool checkExists(string p) => File.Exists(Path.Combine(p, "ipc.txt")); - string stableInstallPath = string.Empty; + string stableInstallPath = string.Empty; + + try + { + try + { + stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + + if (checkExists(stableInstallPath)) + return stableInstallPath; + } + catch + { + } try { - try - { - stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH"); + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - try - { - using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); - - if (checkExists(stableInstallPath)) - return stableInstallPath; - } - catch - { - } - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); if (checkExists(stableInstallPath)) return stableInstallPath; - - stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); - if (checkExists(stableInstallPath)) - return stableInstallPath; - - return null; } - finally + catch { - Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } - } - public StableStorage(DesktopGameHost host) - : base(string.Empty, host) + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu"); + if (checkExists(stableInstallPath)) + return stableInstallPath; + + return null; + } + finally { + Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); } } } diff --git a/osu.Game/IO/OsuStorage.cs b/osu.Game/IO/OsuStorage.cs new file mode 100644 index 0000000000..ee42c491d1 --- /dev/null +++ b/osu.Game/IO/OsuStorage.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Configuration; + +namespace osu.Game.IO +{ + public class OsuStorage : WrappedStorage + { + public OsuStorage(GameHost host) + : base(host.Storage, string.Empty) + { + var storageConfig = new StorageConfigManager(host.Storage); + + var customStoragePath = storageConfig.Get(StorageConfig.FullPath); + + if (!string.IsNullOrEmpty(customStoragePath)) + { + ChangeTargetStorage(host.GetStorage(customStoragePath)); + Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs"); + } + } + } +} diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs new file mode 100644 index 0000000000..705bbf6840 --- /dev/null +++ b/osu.Game/IO/WrappedStorage.cs @@ -0,0 +1,80 @@ +// 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.IO; +using osu.Framework.Platform; + +namespace osu.Game.IO +{ + /// + /// A storage which wraps another storage and delegates implementation, potentially mutating the lookup path. + /// + public class WrappedStorage : Storage + { + protected Storage UnderlyingStorage { get; private set; } + + private readonly string subPath; + + public WrappedStorage(Storage underlyingStorage, string subPath = null) + : base(string.Empty) + { + ChangeTargetStorage(underlyingStorage); + + this.subPath = subPath; + } + + protected virtual string MutatePath(string path) => !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path; + + protected void ChangeTargetStorage(Storage newStorage) + { + UnderlyingStorage = newStorage; + } + + public override string GetFullPath(string path, bool createIfNotExisting = false) => + UnderlyingStorage.GetFullPath(MutatePath(path), createIfNotExisting); + + public override bool Exists(string path) => + UnderlyingStorage.Exists(MutatePath(path)); + + public override bool ExistsDirectory(string path) => + UnderlyingStorage.ExistsDirectory(MutatePath(path)); + + public override void DeleteDirectory(string path) => + UnderlyingStorage.DeleteDirectory(MutatePath(path)); + + public override void Delete(string path) => + UnderlyingStorage.Delete(MutatePath(path)); + + public override IEnumerable GetDirectories(string path) => + UnderlyingStorage.GetDirectories(MutatePath(path)); + + public override IEnumerable GetFiles(string path, string pattern = "*") => + UnderlyingStorage.GetFiles(MutatePath(path), pattern); + + public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => + UnderlyingStorage.GetStream(MutatePath(path), access, mode); + + public override string GetDatabaseConnectionString(string name) => + UnderlyingStorage.GetDatabaseConnectionString(MutatePath(name)); + + public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name)); + + public override void OpenInNativeExplorer() => UnderlyingStorage.OpenInNativeExplorer(); + + public override Storage GetStorageForDirectory(string path) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("Must be non-null and not empty string", nameof(path)); + + if (!path.EndsWith(Path.DirectorySeparatorChar)) + path += Path.DirectorySeparatorChar; + + // create non-existing path. + GetFullPath(path, true); + + return new WrappedStorage(this, path); + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f92db4e111..d9f9e2de42 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -71,8 +71,6 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; - protected StorageConfigManager StorageConfig; - private Container content; protected override Container Content => content; @@ -304,17 +302,7 @@ namespace osu.Game { base.SetHost(host); - StorageConfig = new StorageConfigManager(host.Storage); - - var customStoragePath = StorageConfig.Get(Configuration.StorageConfig.FullPath); - - if (!string.IsNullOrEmpty(customStoragePath)) - { - Storage = new CustomStorage(customStoragePath, host); - Logger.Storage = Storage.GetStorageForDirectory("logs"); - } - else - Storage = host.Storage; + Storage = new OsuStorage(host); if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); @@ -366,17 +354,5 @@ namespace osu.Game public override bool ChangeFocusOnClick => false; } } - - /// - /// A storage pointing to an absolute location specified by the user to store game data files. - /// - private class CustomStorage : NativeStorage - { - public CustomStorage(string fullPath, GameHost host) - : base(string.Empty, host) - { - BasePath = fullPath; - } - } } } From 90e17853a80af4b970a1cae6481c287c124d6fef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 18:12:11 +0900 Subject: [PATCH 4/8] Update tests in line with framework changes --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 2d5f1f238f..b82339281e 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless-{nameof(TestDefaultDirectory)}"); + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless", nameof(TestDefaultDirectory)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } @@ -41,7 +41,7 @@ namespace osu.Game.Tests.NonVisual { using (var host = new HeadlessGameHost(nameof(TestCustomDirectory))) { - string headlessPrefix = $"headless-{nameof(TestCustomDirectory)}"; + string headlessPrefix = Path.Combine("headless", nameof(TestCustomDirectory)); // need access before the game has constructed its own storage yet. Storage storage = new DesktopStorage(headlessPrefix, host); From d21c42a222ffe2f569b66fa7a5deb177374e247a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 20:59:29 +0900 Subject: [PATCH 5/8] 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 8214fa2f2c..af699af1ba 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 91c89cbc20..397d48f7b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9ff7e3fc02..036f87541f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 4ac5ed71f4f82b1f9a4b47677e30e00ac3e230d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 May 2020 21:48:57 +0900 Subject: [PATCH 6/8] Remove redundant string interpolation --- osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index b82339281e..d741bc5de1 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.NonVisual var osu = loadOsu(host); var storage = osu.Dependencies.Get(); - string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, $"headless", nameof(TestDefaultDirectory)); + string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestDefaultDirectory)); Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation)); } From abd1115c6d1dcf068e7eccd48a987dc27fe54839 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 May 2020 19:08:43 +0900 Subject: [PATCH 7/8] Fix test failures --- osu.Game/OsuGameBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d9f9e2de42..cf39c03f9d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -302,7 +302,8 @@ namespace osu.Game { base.SetHost(host); - Storage = new OsuStorage(host); + if (Storage == null) // may be non-null for certain tests + Storage = new OsuStorage(host); if (LocalConfig == null) LocalConfig = new OsuConfigManager(Storage); From 384862d48b5e614488ca4d19354c2481aca74a8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 10 May 2020 13:17:37 +0900 Subject: [PATCH 8/8] Fix incorrect relative paths when using GetStorageForDirectory --- .../NonVisual/CustomDataDirectoryTest.cs | 45 ++++++++++++++++++- osu.Game/IO/WrappedStorage.cs | 12 ++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index d741bc5de1..7c559ea6d2 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -36,6 +37,8 @@ namespace osu.Game.Tests.NonVisual } } + private string customPath => Path.Combine(Environment.CurrentDirectory, "custom-path"); + [Test] public void TestCustomDirectory() { @@ -49,7 +52,7 @@ namespace osu.Game.Tests.NonVisual storage.DeleteDirectory(string.Empty); using (var storageConfig = new StorageConfigManager(storage)) - storageConfig.Set(StorageConfig.FullPath, Path.Combine(Environment.CurrentDirectory, "custom-path")); + storageConfig.Set(StorageConfig.FullPath, customPath); try { @@ -58,7 +61,45 @@ namespace osu.Game.Tests.NonVisual // switch to DI'd storage storage = osu.Dependencies.Get(); - Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(Environment.CurrentDirectory, "custom-path"))); + Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public void TestSubDirectoryLookup() + { + using (var host = new HeadlessGameHost(nameof(TestSubDirectoryLookup))) + { + string headlessPrefix = Path.Combine("headless", nameof(TestSubDirectoryLookup)); + + // need access before the game has constructed its own storage yet. + Storage storage = new DesktopStorage(headlessPrefix, host); + // manual cleaning so we can prepare a config file. + storage.DeleteDirectory(string.Empty); + + using (var storageConfig = new StorageConfigManager(storage)) + storageConfig.Set(StorageConfig.FullPath, customPath); + + try + { + var osu = loadOsu(host); + + // switch to DI'd storage + storage = osu.Dependencies.Get(); + + string actualTestFile = Path.Combine(customPath, "rulesets", "test"); + + File.WriteAllText(actualTestFile, "test"); + + var rulesetStorage = storage.GetStorageForDirectory("rulesets"); + var lookupPath = rulesetStorage.GetFiles(".").Single(); + + Assert.That(lookupPath, Is.EqualTo("test")); } finally { diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 705bbf6840..cc59e2cc28 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -48,10 +48,18 @@ namespace osu.Game.IO UnderlyingStorage.Delete(MutatePath(path)); public override IEnumerable GetDirectories(string path) => - UnderlyingStorage.GetDirectories(MutatePath(path)); + ToLocalRelative(UnderlyingStorage.GetDirectories(MutatePath(path))); + + public IEnumerable ToLocalRelative(IEnumerable paths) + { + string localRoot = GetFullPath(string.Empty); + + foreach (var path in paths) + yield return Path.GetRelativePath(localRoot, UnderlyingStorage.GetFullPath(path)); + } public override IEnumerable GetFiles(string path, string pattern = "*") => - UnderlyingStorage.GetFiles(MutatePath(path), pattern); + ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern)); public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode);