From 6621d363da05951e3208f785f472190802638789 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 May 2020 17:01:05 +0900 Subject: [PATCH] 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; + } + } } }