mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 21:03:21 +08:00
Merge pull request #9256 from MiraiSubject/tourney-asset-refactor
This commit is contained in:
commit
ca1c287664
@ -111,6 +111,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
|
||||
var osu = LoadOsuIntoHost(host);
|
||||
var storage = osu.Dependencies.Get<Storage>();
|
||||
var osuStorage = storage as MigratableStorage;
|
||||
|
||||
// Store the current storage's path. We'll need to refer to this for assertions in the original directory after the migration completes.
|
||||
string originalDirectory = storage.GetFullPath(".");
|
||||
@ -137,13 +138,15 @@ namespace osu.Game.Tests.NonVisual
|
||||
Assert.That(!Directory.Exists(Path.Combine(originalDirectory, "test-nested", "cache")));
|
||||
Assert.That(storage.ExistsDirectory(Path.Combine("test-nested", "cache")));
|
||||
|
||||
foreach (var file in OsuStorage.IGNORE_FILES)
|
||||
Assert.That(osuStorage, Is.Not.Null);
|
||||
|
||||
foreach (var file in osuStorage.IgnoreFiles)
|
||||
{
|
||||
Assert.That(File.Exists(Path.Combine(originalDirectory, file)));
|
||||
Assert.That(storage.Exists(file), Is.False);
|
||||
}
|
||||
|
||||
foreach (var dir in OsuStorage.IGNORE_DIRECTORIES)
|
||||
foreach (var dir in osuStorage.IgnoreDirectories)
|
||||
{
|
||||
Assert.That(Directory.Exists(Path.Combine(originalDirectory, dir)));
|
||||
Assert.That(storage.ExistsDirectory(dir), Is.False);
|
||||
|
@ -0,0 +1,169 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Tournament.Configuration;
|
||||
using osu.Game.Tests;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class CustomTourneyDirectoryTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDefaultDirectory()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
{
|
||||
try
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
var storage = osu.Dependencies.Get<Storage>();
|
||||
|
||||
Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomDirectory()
|
||||
{
|
||||
using (HeadlessGameHost host = new HeadlessGameHost(nameof(TestCustomDirectory))) // don't use clean run as we are writing a config file.
|
||||
{
|
||||
string osuDesktopStorage = basePath(nameof(TestCustomDirectory));
|
||||
const string custom_tournament = "custom";
|
||||
|
||||
// need access before the game has constructed its own storage yet.
|
||||
Storage storage = new DesktopStorage(osuDesktopStorage, host);
|
||||
// manual cleaning so we can prepare a config file.
|
||||
storage.DeleteDirectory(string.Empty);
|
||||
|
||||
using (var storageConfig = new TournamentStorageManager(storage))
|
||||
storageConfig.Set(StorageConfig.CurrentTournament, custom_tournament);
|
||||
|
||||
try
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
|
||||
storage = osu.Dependencies.Get<Storage>();
|
||||
|
||||
Assert.That(storage.GetFullPath("."), Is.EqualTo(Path.Combine(host.Storage.GetFullPath("."), "tournaments", custom_tournament)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMigration()
|
||||
{
|
||||
using (HeadlessGameHost host = new HeadlessGameHost(nameof(TestMigration))) // don't use clean run as we are writing test files for migration.
|
||||
{
|
||||
string osuRoot = basePath(nameof(TestMigration));
|
||||
string configFile = Path.Combine(osuRoot, "tournament.ini");
|
||||
|
||||
if (File.Exists(configFile))
|
||||
File.Delete(configFile);
|
||||
|
||||
// Recreate the old setup that uses "tournament" as the base path.
|
||||
string oldPath = Path.Combine(osuRoot, "tournament");
|
||||
|
||||
string videosPath = Path.Combine(oldPath, "videos");
|
||||
string modsPath = Path.Combine(oldPath, "mods");
|
||||
string flagsPath = Path.Combine(oldPath, "flags");
|
||||
|
||||
Directory.CreateDirectory(videosPath);
|
||||
Directory.CreateDirectory(modsPath);
|
||||
Directory.CreateDirectory(flagsPath);
|
||||
|
||||
// Define testing files corresponding to the specific file migrations that are needed
|
||||
string bracketFile = Path.Combine(osuRoot, "bracket.json");
|
||||
|
||||
string drawingsConfig = Path.Combine(osuRoot, "drawings.ini");
|
||||
string drawingsFile = Path.Combine(osuRoot, "drawings.txt");
|
||||
string drawingsResult = Path.Combine(osuRoot, "drawings_results.txt");
|
||||
|
||||
// Define sample files to test recursive copying
|
||||
string videoFile = Path.Combine(videosPath, "video.mp4");
|
||||
string modFile = Path.Combine(modsPath, "mod.png");
|
||||
string flagFile = Path.Combine(flagsPath, "flag.png");
|
||||
|
||||
File.WriteAllText(bracketFile, "{}");
|
||||
File.WriteAllText(drawingsConfig, "test");
|
||||
File.WriteAllText(drawingsFile, "test");
|
||||
File.WriteAllText(drawingsResult, "test");
|
||||
File.WriteAllText(videoFile, "test");
|
||||
File.WriteAllText(modFile, "test");
|
||||
File.WriteAllText(flagFile, "test");
|
||||
|
||||
try
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
|
||||
var storage = osu.Dependencies.Get<Storage>();
|
||||
|
||||
string migratedPath = Path.Combine(host.Storage.GetFullPath("."), "tournaments", "default");
|
||||
|
||||
videosPath = Path.Combine(migratedPath, "videos");
|
||||
modsPath = Path.Combine(migratedPath, "mods");
|
||||
flagsPath = Path.Combine(migratedPath, "flags");
|
||||
|
||||
videoFile = Path.Combine(videosPath, "video.mp4");
|
||||
modFile = Path.Combine(modsPath, "mod.png");
|
||||
flagFile = Path.Combine(flagsPath, "flag.png");
|
||||
|
||||
Assert.That(storage.GetFullPath("."), Is.EqualTo(migratedPath));
|
||||
|
||||
Assert.True(storage.Exists("bracket.json"));
|
||||
Assert.True(storage.Exists("drawings.txt"));
|
||||
Assert.True(storage.Exists("drawings_results.txt"));
|
||||
|
||||
Assert.True(storage.Exists("drawings.ini"));
|
||||
|
||||
Assert.True(storage.Exists(videoFile));
|
||||
Assert.True(storage.Exists(modFile));
|
||||
Assert.True(storage.Exists(flagFile));
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Storage.Delete("tournament.ini");
|
||||
host.Storage.DeleteDirectory("tournaments");
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TournamentGameBase loadOsu(GameHost host)
|
||||
{
|
||||
var osu = new TournamentGameBase();
|
||||
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<bool> result, string failureMessage, int timeout = 90000)
|
||||
{
|
||||
Task task = Task.Run(() =>
|
||||
{
|
||||
while (!result()) Thread.Sleep(200);
|
||||
});
|
||||
|
||||
Assert.IsTrue(task.Wait(timeout), failureMessage);
|
||||
}
|
||||
|
||||
private string basePath(string testInstance) => Path.Combine(RuntimeInfo.StartupDirectory, "headless", testInstance);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Video;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Tournament.IO;
|
||||
|
||||
namespace osu.Game.Tournament.Components
|
||||
{
|
||||
@ -17,7 +18,6 @@ namespace osu.Game.Tournament.Components
|
||||
private readonly string filename;
|
||||
private readonly bool drawFallbackGradient;
|
||||
private Video video;
|
||||
|
||||
private ManualClock manualClock;
|
||||
|
||||
public TourneyVideo(string filename, bool drawFallbackGradient = false)
|
||||
@ -27,9 +27,9 @@ namespace osu.Game.Tournament.Components
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TournamentStorage storage)
|
||||
private void load(TournamentVideoResourceStore storage)
|
||||
{
|
||||
var stream = storage.GetStream($@"videos/{filename}");
|
||||
var stream = storage.GetStream(filename);
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
|
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Tournament.Configuration
|
||||
{
|
||||
public class TournamentStorageManager : IniConfigManager<StorageConfig>
|
||||
{
|
||||
protected override string Filename => "tournament.ini";
|
||||
|
||||
public TournamentStorageManager(Storage storage)
|
||||
: base(storage)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public enum StorageConfig
|
||||
{
|
||||
CurrentTournament,
|
||||
}
|
||||
}
|
72
osu.Game.Tournament/IO/TournamentStorage.cs
Normal file
72
osu.Game.Tournament/IO/TournamentStorage.cs
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.IO;
|
||||
using System.IO;
|
||||
using osu.Game.Tournament.Configuration;
|
||||
|
||||
namespace osu.Game.Tournament.IO
|
||||
{
|
||||
public class TournamentStorage : MigratableStorage
|
||||
{
|
||||
private const string default_tournament = "default";
|
||||
private readonly Storage storage;
|
||||
private readonly TournamentStorageManager storageConfig;
|
||||
|
||||
public TournamentStorage(Storage storage)
|
||||
: base(storage.GetStorageForDirectory("tournaments"), string.Empty)
|
||||
{
|
||||
this.storage = storage;
|
||||
|
||||
storageConfig = new TournamentStorageManager(storage);
|
||||
|
||||
if (storage.Exists("tournament.ini"))
|
||||
{
|
||||
ChangeTargetStorage(UnderlyingStorage.GetStorageForDirectory(storageConfig.Get<string>(StorageConfig.CurrentTournament)));
|
||||
}
|
||||
else
|
||||
Migrate(UnderlyingStorage.GetStorageForDirectory(default_tournament));
|
||||
|
||||
Logger.Log("Using tournament storage: " + GetFullPath(string.Empty));
|
||||
}
|
||||
|
||||
public override void Migrate(Storage newStorage)
|
||||
{
|
||||
// this migration only happens once on moving to the per-tournament storage system.
|
||||
// listed files are those known at that point in time.
|
||||
// this can be removed at some point in the future (6 months obsoletion would mean 2021-04-19)
|
||||
|
||||
var source = new DirectoryInfo(storage.GetFullPath("tournament"));
|
||||
var destination = new DirectoryInfo(newStorage.GetFullPath("."));
|
||||
|
||||
if (source.Exists)
|
||||
{
|
||||
Logger.Log("Migrating tournament assets to default tournament storage.");
|
||||
CopyRecursive(source, destination);
|
||||
DeleteRecursive(source);
|
||||
}
|
||||
|
||||
moveFileIfExists("bracket.json", destination);
|
||||
moveFileIfExists("drawings.txt", destination);
|
||||
moveFileIfExists("drawings_results.txt", destination);
|
||||
moveFileIfExists("drawings.ini", destination);
|
||||
|
||||
ChangeTargetStorage(newStorage);
|
||||
storageConfig.Set(StorageConfig.CurrentTournament, default_tournament);
|
||||
storageConfig.Save();
|
||||
}
|
||||
|
||||
private void moveFileIfExists(string file, DirectoryInfo destination)
|
||||
{
|
||||
if (!storage.Exists(file))
|
||||
return;
|
||||
|
||||
Logger.Log($"Migrating {file} to default tournament storage.");
|
||||
var fileInfo = new System.IO.FileInfo(storage.GetFullPath(file));
|
||||
AttemptOperation(() => fileInfo.CopyTo(Path.Combine(destination.FullName, fileInfo.Name), true));
|
||||
fileInfo.Delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,12 +4,12 @@
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Tournament
|
||||
namespace osu.Game.Tournament.IO
|
||||
{
|
||||
internal class TournamentStorage : NamespacedResourceStore<byte[]>
|
||||
public class TournamentVideoResourceStore : NamespacedResourceStore<byte[]>
|
||||
{
|
||||
public TournamentStorage(Storage storage)
|
||||
: base(new StorageBackedResourceStore(storage), "tournament")
|
||||
public TournamentVideoResourceStore(Storage storage)
|
||||
: base(new StorageBackedResourceStore(storage), "videos")
|
||||
{
|
||||
AddExtension("m4v");
|
||||
AddExtension("avi");
|
@ -8,11 +8,12 @@ using Newtonsoft.Json;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Tournament.IPC;
|
||||
using osu.Game.Tournament.IO;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Users;
|
||||
using osuTK.Input;
|
||||
@ -23,13 +24,8 @@ namespace osu.Game.Tournament
|
||||
public class TournamentGameBase : OsuGameBase
|
||||
{
|
||||
private const string bracket_filename = "bracket.json";
|
||||
|
||||
private LadderInfo ladder;
|
||||
|
||||
private Storage storage;
|
||||
|
||||
private TournamentStorage tournamentStorage;
|
||||
|
||||
private TournamentStorage storage;
|
||||
private DependencyContainer dependencies;
|
||||
private FileBasedIPC ipc;
|
||||
|
||||
@ -39,15 +35,14 @@ namespace osu.Game.Tournament
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
private void load(Storage baseStorage)
|
||||
{
|
||||
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
|
||||
|
||||
dependencies.CacheAs(tournamentStorage = new TournamentStorage(storage));
|
||||
dependencies.CacheAs<Storage>(storage = new TournamentStorage(baseStorage));
|
||||
dependencies.Cache(new TournamentVideoResourceStore(storage));
|
||||
|
||||
Textures.AddStore(new TextureLoaderStore(tournamentStorage));
|
||||
|
||||
this.storage = storage;
|
||||
Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage)));
|
||||
|
||||
readBracket();
|
||||
|
||||
|
132
osu.Game/IO/MigratableStorage.cs
Normal file
132
osu.Game/IO/MigratableStorage.cs
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="WrappedStorage"/> that is migratable to different locations.
|
||||
/// </summary>
|
||||
public abstract class MigratableStorage : WrappedStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// A relative list of directory paths which should not be migrated.
|
||||
/// </summary>
|
||||
public virtual string[] IgnoreDirectories => Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// A relative list of file paths which should not be migrated.
|
||||
/// </summary>
|
||||
public virtual string[] IgnoreFiles => Array.Empty<string>();
|
||||
|
||||
protected MigratableStorage(Storage storage, string subPath = null)
|
||||
: base(storage, subPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A general purpose migration method to move the storage to a different location.
|
||||
/// <param name="newStorage">The target storage of the migration.</param>
|
||||
/// </summary>
|
||||
public virtual void Migrate(Storage newStorage)
|
||||
{
|
||||
var source = new DirectoryInfo(GetFullPath("."));
|
||||
var destination = new DirectoryInfo(newStorage.GetFullPath("."));
|
||||
|
||||
// using Uri is the easiest way to check equality and contains (https://stackoverflow.com/a/7710620)
|
||||
var sourceUri = new Uri(source.FullName + Path.DirectorySeparatorChar);
|
||||
var destinationUri = new Uri(destination.FullName + Path.DirectorySeparatorChar);
|
||||
|
||||
if (sourceUri == destinationUri)
|
||||
throw new ArgumentException("Destination provided is already the current location", destination.FullName);
|
||||
|
||||
if (sourceUri.IsBaseOf(destinationUri))
|
||||
throw new ArgumentException("Destination provided is inside the source", destination.FullName);
|
||||
|
||||
// ensure the new location has no files present, else hard abort
|
||||
if (destination.Exists)
|
||||
{
|
||||
if (destination.GetFiles().Length > 0 || destination.GetDirectories().Length > 0)
|
||||
throw new ArgumentException("Destination provided already has files or directories present", destination.FullName);
|
||||
}
|
||||
|
||||
CopyRecursive(source, destination);
|
||||
ChangeTargetStorage(newStorage);
|
||||
DeleteRecursive(source);
|
||||
}
|
||||
|
||||
protected void DeleteRecursive(DirectoryInfo target, bool topLevelExcludes = true)
|
||||
{
|
||||
foreach (System.IO.FileInfo fi in target.GetFiles())
|
||||
{
|
||||
if (topLevelExcludes && IgnoreFiles.Contains(fi.Name))
|
||||
continue;
|
||||
|
||||
AttemptOperation(() => fi.Delete());
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo dir in target.GetDirectories())
|
||||
{
|
||||
if (topLevelExcludes && IgnoreDirectories.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
AttemptOperation(() => dir.Delete(true));
|
||||
}
|
||||
|
||||
if (target.GetFiles().Length == 0 && target.GetDirectories().Length == 0)
|
||||
AttemptOperation(target.Delete);
|
||||
}
|
||||
|
||||
protected void CopyRecursive(DirectoryInfo source, DirectoryInfo destination, bool topLevelExcludes = true)
|
||||
{
|
||||
// based off example code https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo
|
||||
if (!destination.Exists)
|
||||
Directory.CreateDirectory(destination.FullName);
|
||||
|
||||
foreach (System.IO.FileInfo fi in source.GetFiles())
|
||||
{
|
||||
if (topLevelExcludes && IgnoreFiles.Contains(fi.Name))
|
||||
continue;
|
||||
|
||||
AttemptOperation(() => fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true));
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
{
|
||||
if (topLevelExcludes && IgnoreDirectories.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
CopyRecursive(dir, destination.CreateSubdirectory(dir.Name), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt an IO operation multiple times and only throw if none of the attempts succeed.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="attempts">The number of attempts (250ms wait between each).</param>
|
||||
protected static void AttemptOperation(Action action, int attempts = 10)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts-- == 0)
|
||||
throw;
|
||||
}
|
||||
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@ -13,7 +10,7 @@ using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
public class OsuStorage : WrappedStorage
|
||||
public class OsuStorage : MigratableStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the error (if any) that occurred when initialising the custom storage during initial startup.
|
||||
@ -36,9 +33,9 @@ namespace osu.Game.IO
|
||||
private readonly StorageConfigManager storageConfig;
|
||||
private readonly Storage defaultStorage;
|
||||
|
||||
public static readonly string[] IGNORE_DIRECTORIES = { "cache" };
|
||||
public override string[] IgnoreDirectories => new[] { "cache" };
|
||||
|
||||
public static readonly string[] IGNORE_FILES =
|
||||
public override string[] IgnoreFiles => new[]
|
||||
{
|
||||
"framework.ini",
|
||||
"storage.ini"
|
||||
@ -103,106 +100,11 @@ namespace osu.Game.IO
|
||||
Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs");
|
||||
}
|
||||
|
||||
public void Migrate(string newLocation)
|
||||
public override void Migrate(Storage newStorage)
|
||||
{
|
||||
var source = new DirectoryInfo(GetFullPath("."));
|
||||
var destination = new DirectoryInfo(newLocation);
|
||||
|
||||
// using Uri is the easiest way to check equality and contains (https://stackoverflow.com/a/7710620)
|
||||
var sourceUri = new Uri(source.FullName + Path.DirectorySeparatorChar);
|
||||
var destinationUri = new Uri(destination.FullName + Path.DirectorySeparatorChar);
|
||||
|
||||
if (sourceUri == destinationUri)
|
||||
throw new ArgumentException("Destination provided is already the current location", nameof(newLocation));
|
||||
|
||||
if (sourceUri.IsBaseOf(destinationUri))
|
||||
throw new ArgumentException("Destination provided is inside the source", nameof(newLocation));
|
||||
|
||||
// ensure the new location has no files present, else hard abort
|
||||
if (destination.Exists)
|
||||
{
|
||||
if (destination.GetFiles().Length > 0 || destination.GetDirectories().Length > 0)
|
||||
throw new ArgumentException("Destination provided already has files or directories present", nameof(newLocation));
|
||||
|
||||
deleteRecursive(destination);
|
||||
}
|
||||
|
||||
copyRecursive(source, destination);
|
||||
|
||||
ChangeTargetStorage(host.GetStorage(newLocation));
|
||||
|
||||
storageConfig.Set(StorageConfig.FullPath, newLocation);
|
||||
base.Migrate(newStorage);
|
||||
storageConfig.Set(StorageConfig.FullPath, newStorage.GetFullPath("."));
|
||||
storageConfig.Save();
|
||||
|
||||
deleteRecursive(source);
|
||||
}
|
||||
|
||||
private static void deleteRecursive(DirectoryInfo target, bool topLevelExcludes = true)
|
||||
{
|
||||
foreach (System.IO.FileInfo fi in target.GetFiles())
|
||||
{
|
||||
if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name))
|
||||
continue;
|
||||
|
||||
attemptOperation(() => fi.Delete());
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo dir in target.GetDirectories())
|
||||
{
|
||||
if (topLevelExcludes && IGNORE_DIRECTORIES.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
attemptOperation(() => dir.Delete(true));
|
||||
}
|
||||
|
||||
if (target.GetFiles().Length == 0 && target.GetDirectories().Length == 0)
|
||||
attemptOperation(target.Delete);
|
||||
}
|
||||
|
||||
private static void copyRecursive(DirectoryInfo source, DirectoryInfo destination, bool topLevelExcludes = true)
|
||||
{
|
||||
// based off example code https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo
|
||||
Directory.CreateDirectory(destination.FullName);
|
||||
|
||||
foreach (System.IO.FileInfo fi in source.GetFiles())
|
||||
{
|
||||
if (topLevelExcludes && IGNORE_FILES.Contains(fi.Name))
|
||||
continue;
|
||||
|
||||
attemptOperation(() => fi.CopyTo(Path.Combine(destination.FullName, fi.Name), true));
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo dir in source.GetDirectories())
|
||||
{
|
||||
if (topLevelExcludes && IGNORE_DIRECTORIES.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
copyRecursive(dir, destination.CreateSubdirectory(dir.Name), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt an IO operation multiple times and only throw if none of the attempts succeed.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="attempts">The number of attempts (250ms wait between each).</param>
|
||||
private static void attemptOperation(Action action, int attempts = 10)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (attempts-- == 0)
|
||||
throw;
|
||||
}
|
||||
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ namespace osu.Game
|
||||
public void Migrate(string path)
|
||||
{
|
||||
contextFactory.FlushConnections();
|
||||
(Storage as OsuStorage)?.Migrate(path);
|
||||
(Storage as OsuStorage)?.Migrate(Host.GetStorage(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user