Skip to content

Commit

Permalink
Added: Experimental Support for Portable Mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Sewer56 committed Feb 23, 2022
1 parent 50afede commit f701528
Show file tree
Hide file tree
Showing 18 changed files with 117 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public async void Execute(object? parameter)

// Get paths.
var loaderConfig = IoC.Get<LoaderConfig>();
string applicationConfigDirectory = loaderConfig.ApplicationConfigDirectory;
string applicationConfigDirectory = loaderConfig.GetApplicationConfigDirectory();
string applicationDirectory = Path.Combine(applicationConfigDirectory, config.AppId);
string applicationConfigFile = Path.Combine(applicationDirectory, ApplicationConfig.ConfigFileName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public CreateModViewModel(ModConfigService modConfigService)
return null;

var config = new ModConfig() { ModId = ModId };
var modDirectory = Path.Combine(IoC.Get<LoaderConfig>().ModConfigDirectory, IOEx.ForceValidFilePath(ModId));
var modDirectory = Path.Combine(IoC.Get<LoaderConfig>().GetModConfigDirectory(), IOEx.ForceValidFilePath(ModId));
var filePath = Path.Combine(modDirectory, ModConfig.ConfigFileName);
await IConfig<ModConfig>.ToPathAsync(config, filePath);
return new PathTuple<ModConfig>(filePath, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ public class DownloadPackageViewModel : ObservableObject
public DownloadPackageViewModel(IDownloadablePackage package, LoaderConfig config)
{
Packages.Add(package);
_modsFolder = config.ModConfigDirectory;
_modsFolder = config.GetModConfigDirectory();
}

/// <inheritdoc />
public DownloadPackageViewModel(IEnumerable<IDownloadablePackage> packages, LoaderConfig config)
{
Packages.AddRange(packages);
_modsFolder = config.ModConfigDirectory;
_modsFolder = config.GetModConfigDirectory();
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Launcher.Lib/Setup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ private static void SetLoaderPaths(LoaderConfig config, string launcherDirectory
config.Bootstrapper64Path = bootstrapper64Path;

// Update Environment Variables
Task.Run(() => Environment.SetEnvironmentVariable("RELOADEDIIMODS", config.ModConfigDirectory, EnvironmentVariableTarget.User));
Task.Run(() => Environment.SetEnvironmentVariable("RELOADEDIIMODS", config.GetModConfigDirectory(), EnvironmentVariableTarget.User));
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Launcher.Lib/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private static void LaunchApplicationAndExit(string applicationToLaunch)
arguments ??= "";
applicationToLaunch = Path.GetFullPath(applicationToLaunch);

var application = ApplicationConfig.GetAllApplications(IoC.Get<LoaderConfig>().ApplicationConfigDirectory).FirstOrDefault(x => ApplicationConfig.GetAbsoluteAppLocation(x) == applicationToLaunch);
var application = ApplicationConfig.GetAllApplications(IoC.Get<LoaderConfig>().GetApplicationConfigDirectory()).FirstOrDefault(x => ApplicationConfig.GetAbsoluteAppLocation(x) == applicationToLaunch);
if (application != null)
arguments = $"{arguments} {application.Config.AppArguments}";

Expand Down
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Loader.IO/Config/ApplicationConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static bool TryGetApplicationIcon(string configPath, ApplicationConfig co
public static List<PathTuple<ApplicationConfig>> GetAllApplications(string appDirectory = null, CancellationToken token = default)
{
if (appDirectory == null)
appDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).ApplicationConfigDirectory;
appDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).GetApplicationConfigDirectory();

return ConfigReader<ApplicationConfig>.ReadConfigurations(appDirectory, ConfigFileName, token, 2);
}
Expand Down
94 changes: 82 additions & 12 deletions source/Reloaded.Mod.Loader.IO/Config/LoaderConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class LoaderConfig : ObservableObject, IConfig<LoaderConfig>
private const string DefaultPluginConfigDirectory = "Plugins";
private const string DefaultLanguageFile = "en-GB.xaml";
private const string DefaultThemeFile = "Default.xaml";
private const string PortableModeFile = "portable.txt";

private static readonly NugetFeed[] DefaultFeeds = new NugetFeed[]
{
Expand Down Expand Up @@ -55,22 +56,26 @@ public class LoaderConfig : ObservableObject, IConfig<LoaderConfig>
/// <summary>
/// The directory which houses all Reloaded Application information (e.g. Games etc.)
/// </summary>
public string ApplicationConfigDirectory { get; set; } = Empty;
[JsonInclude]
public string ApplicationConfigDirectory { private get; set; } = Empty;

/// <summary>
/// Contains the directory which houses all Reloaded mods.
/// </summary>
public string ModUserConfigDirectory { get; set; } = Empty;
[JsonInclude]
public string ModUserConfigDirectory { private get; set; } = Empty;

/// <summary>
/// Contains the directory which houses all Reloaded plugins.
/// </summary>
public string PluginConfigDirectory { get; set; } = Empty;
[JsonInclude]
public string PluginConfigDirectory { private get; set; } = Empty;

/// <summary>
/// Contains the directory which houses all Reloaded mods.
/// </summary>
public string ModConfigDirectory { get; set; } = Empty;
[JsonInclude]
public string ModConfigDirectory { private get; set; } = Empty;

/// <summary>
/// Contains a list of all plugins that are enabled, by config paths relative to plugin directory.
Expand Down Expand Up @@ -140,6 +145,15 @@ public class LoaderConfig : ObservableObject, IConfig<LoaderConfig>
/// </summary>
public int ProcessRefreshInterval { get; set; } = 200;

/// <summary>
/// True if the loader is in `Portable Mode`.
/// In Portable mode, the locations of <see cref="ApplicationConfigDirectory"/>, <see cref="ModConfigDirectory"/>, <see cref="ModUserConfigDirectory"/> and <see cref="PluginConfigDirectory"/>
/// are ignored and replaced with default, `Apps`, `Mods` and `Plugins` folders.
/// </summary>
[JsonIgnore]
public bool UsePortableMode { get; set; }

private string? _launcherFolder;

/* Some mods are universal :wink: */

Expand All @@ -155,6 +169,14 @@ public void SanitizeConfig()
RerouteDefaultFeed();
}

public string GetModUserConfigDirectory() => UsePortableMode ? GetDirectoryRelativeToLauncherFolder(DefaultModUserConfigDirectory) : ModUserConfigDirectory;

public string GetPluginConfigDirectory() => UsePortableMode ? GetDirectoryRelativeToLauncherFolder(DefaultPluginConfigDirectory) : PluginConfigDirectory;

public string GetApplicationConfigDirectory() => UsePortableMode ? GetDirectoryRelativeToLauncherFolder(DefaultApplicationConfigDirectory) : ApplicationConfigDirectory;

public string GetModConfigDirectory() => UsePortableMode ? GetDirectoryRelativeToLauncherFolder(DefaultModConfigDirectory) : ModConfigDirectory;

private void RerouteDefaultFeed()
{
foreach (var feed in NuGetFeeds)
Expand All @@ -169,10 +191,20 @@ private void ResetMissingDirectories()
{
try
{
ApplicationConfigDirectory = IfNotExistsMakeDefaultDirectory(ApplicationConfigDirectory, DefaultApplicationConfigDirectory);
ModUserConfigDirectory = IfNotExistsMakeDefaultDirectory(ModUserConfigDirectory, DefaultModUserConfigDirectory);
ModConfigDirectory = IfNotExistsMakeDefaultDirectory(ModConfigDirectory, DefaultModConfigDirectory);
PluginConfigDirectory = IfNotExistsMakeDefaultDirectory(PluginConfigDirectory, DefaultPluginConfigDirectory);
UsePortableMode = IsInPortableMode();
if (UsePortableMode)
{
// Create default directories (if needed).
Directory.CreateDirectory(GetApplicationConfigDirectory());
Directory.CreateDirectory(GetModConfigDirectory());
Directory.CreateDirectory(GetModUserConfigDirectory());
Directory.CreateDirectory(GetPluginConfigDirectory());
}

ApplicationConfigDirectory = SetDefaultDirectory(ApplicationConfigDirectory, DefaultApplicationConfigDirectory);
ModUserConfigDirectory = SetDefaultDirectory(ModUserConfigDirectory, DefaultModUserConfigDirectory);
ModConfigDirectory = SetDefaultDirectory(ModConfigDirectory, DefaultModConfigDirectory);
PluginConfigDirectory = SetDefaultDirectory(PluginConfigDirectory, DefaultPluginConfigDirectory);
}
catch (Exception)
{
Expand All @@ -187,22 +219,60 @@ private void CleanEmptyFeeds()
}

// Sets default directory if does not exist.
private static string IfNotExistsMakeDefaultDirectory(string directoryPath, string defaultDirectory)
private string SetDefaultDirectory(string directoryPath, string defaultDirectory)
{
if (!Directory.Exists(directoryPath))
return CreateDirectoryRelativeToProgram(defaultDirectory);
return CreateDirectoryRelativeToLauncherFolder(defaultDirectory);

return directoryPath;
}

/// <summary>
/// Gets a directory relative to the path of the program.
/// </summary>
private string GetDirectoryRelativeToLauncherFolder(string directoryPath)
{
return Path.GetFullPath(Path.Combine(GetLauncherFolder(), directoryPath));
}

/// <summary>
/// Creates a directory relative to the current assembly directory.
/// Returns the full path of the supplied directory parameter.
/// </summary>
private static string CreateDirectoryRelativeToProgram(string directoryPath)
private string CreateDirectoryRelativeToLauncherFolder(string directoryPath)
{
string fullDirectoryPath = Path.GetFullPath(Path.Combine(Paths.CurrentProgramFolder, directoryPath));
string fullDirectoryPath = GetDirectoryRelativeToLauncherFolder(directoryPath);
Directory.CreateDirectory(fullDirectoryPath);
return fullDirectoryPath;
}

/// <summary>
/// Returns true if portable mode (always use launcher folder) is enabled.
/// </summary>
private bool IsInPortableMode()
{
var currentFolderPath = Path.GetFullPath(Path.Combine(GetLauncherFolder(), PortableModeFile));
return File.Exists(currentFolderPath);
}

private string GetLauncherFolder()
{
if (_launcherFolder != null)
return _launcherFolder;

// Workaround for when non-launcher folder is used.
// e.g. When using loader.
var launcherFolder = NormalizePath(Paths.CurrentProgramFolder);
var launcherFolderFallback = NormalizePath(Path.GetDirectoryName(LauncherPath));
if (!launcherFolder.Equals(launcherFolderFallback, StringComparison.OrdinalIgnoreCase))
launcherFolder = launcherFolderFallback;

_launcherFolder = launcherFolder;
return launcherFolder;

static string NormalizePath(string path)
{
return Path.GetFullPath(new Uri(path).LocalPath).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
}
}
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Loader.IO/Config/ModConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public static bool TryGetIconPath(string configPath, ModConfig modConfig, out st
public static List<PathTuple<ModConfig>> GetAllMods(string modDirectory = null, CancellationToken token = default)
{
if (modDirectory == null)
modDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).ModConfigDirectory;
modDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).GetModConfigDirectory();

return ConfigReader<ModConfig>.ReadConfigurations(modDirectory, ConfigFileName, token, 2, 2);
}
Expand Down
4 changes: 2 additions & 2 deletions source/Reloaded.Mod.Loader.IO/Config/ModUserConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ModUserConfig : ObservableObject, IConfig<ModUserConfig>, IModUserC
public static List<PathTuple<ModUserConfig>> GetAllUserConfigs(string configDirectory = null, CancellationToken token = default)
{
if (configDirectory == null)
configDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).ApplicationConfigDirectory;
configDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).GetApplicationConfigDirectory();

return ConfigReader<ModUserConfig>.ReadConfigurations(configDirectory, ConfigFileName, token, 2);
}
Expand All @@ -50,7 +50,7 @@ public static List<PathTuple<ModUserConfig>> GetAllUserConfigs(string configDire
public static string GetUserConfigFolderForMod(string modId, string configDirectory = null)
{
if (configDirectory == null)
configDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).ModUserConfigDirectory;
configDirectory = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath).GetModUserConfigDirectory();

return Path.Combine(configDirectory, IOEx.ForceValidFilePath(modId));
}
Expand Down
4 changes: 2 additions & 2 deletions source/Reloaded.Mod.Loader.IO/Reloaded.Mod.Loader.IO.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Version>2.0.0</Version>
<TargetFramework>net5.0</TargetFramework>
<Version>2.1.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>preview</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ApplicationConfigService : ConfigServiceBase<ApplicationConfig>
/// <param name="context">Context to which background events should be synchronized.</param>
public ApplicationConfigService(LoaderConfig config, SynchronizationContext context = null)
{
Initialize(config.ApplicationConfigDirectory, ApplicationConfig.ConfigFileName, GetAllConfigs, context);
Initialize(config.GetApplicationConfigDirectory(), ApplicationConfig.ConfigFileName, GetAllConfigs, context);
}

private List<PathTuple<ApplicationConfig>> GetAllConfigs() => ApplicationConfig.GetAllApplications(base.ConfigDirectory);
Expand Down
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Loader.IO/Services/ModConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public ModConfigService(LoaderConfig config, SynchronizationContext context = nu
this.OnAddItem += OnAddItemHandler;
this.OnRemoveItem += OnRemoveItemHandler;

Initialize(config.ModConfigDirectory, ModConfig.ConfigFileName, GetAllConfigs, context);
Initialize(config.GetModConfigDirectory(), ModConfig.ConfigFileName, GetAllConfigs, context);
SetItemsById();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public ModUserConfigService(LoaderConfig config, ModConfigService modConfigServi
this.OnAddItem += OnAddItemHandler;
this.OnRemoveItem += OnRemoveItemHandler;

Initialize(config.ModUserConfigDirectory, ModUserConfig.ConfigFileName, GetAllConfigs, context);
Initialize(config.GetModUserConfigDirectory(), ModUserConfig.ConfigFileName, GetAllConfigs, context);
SetItemsById();

_modConfigService = modConfigService;
Expand Down
12 changes: 6 additions & 6 deletions source/Reloaded.Mod.Loader.Tests/IO/LoaderConfigReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public void ReadWriteConfig()
Assert.Equal(config.LauncherPath, newConfig.LauncherPath);
Assert.Equal(config.Bootstrapper32Path, newConfig.Bootstrapper32Path);
Assert.Equal(config.Bootstrapper64Path, newConfig.Bootstrapper64Path);
Assert.Equal(config.ApplicationConfigDirectory, newConfig.ApplicationConfigDirectory);
Assert.Equal(config.PluginConfigDirectory, newConfig.PluginConfigDirectory);
Assert.Equal(config.ModConfigDirectory, newConfig.ModConfigDirectory);
Assert.Equal(config.GetApplicationConfigDirectory(), newConfig.GetApplicationConfigDirectory());
Assert.Equal(config.GetPluginConfigDirectory(), newConfig.GetPluginConfigDirectory());
Assert.Equal(config.GetModConfigDirectory(), newConfig.GetModConfigDirectory());
Assert.Equal(config.LanguageFile, newConfig.LanguageFile);
Assert.Equal(config.ThemeFile, newConfig.ThemeFile);
}
Expand All @@ -58,8 +58,8 @@ public void ValidDirectoryOnDeserialization()
var newConfig = IConfig<LoaderConfig>.FromPath(Paths.LoaderConfigPath);

// Restore old config and assert.
Assert.True(Directory.Exists(newConfig.ApplicationConfigDirectory));
Assert.True(Directory.Exists(newConfig.ModConfigDirectory));
Assert.True(Directory.Exists(newConfig.PluginConfigDirectory));
Assert.True(Directory.Exists(newConfig.GetApplicationConfigDirectory()));
Assert.True(Directory.Exists(newConfig.GetModConfigDirectory()));
Assert.True(Directory.Exists(newConfig.GetPluginConfigDirectory()));
}
}
12 changes: 6 additions & 6 deletions source/Reloaded.Mod.Loader.Tests/SETUP/TestEnvironmoent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public TestEnvironmoent()
OriginalConfig = IConfig<LoaderConfig>.FromPathOrDefault(Paths.LoaderConfigPath);
TestConfig = MakeTestConfig();

if (OriginalConfig.ApplicationConfigDirectory != TestConfig.ApplicationConfigDirectory)
if (OriginalConfig.GetApplicationConfigDirectory() != TestConfig.GetApplicationConfigDirectory())
IConfig<LoaderConfig>.ToPath(OriginalConfig, $"{Paths.LoaderConfigPath}.bak");

IConfig<LoaderConfig>.ToPath(TestConfig, Paths.LoaderConfigPath);
Expand All @@ -104,7 +104,7 @@ public TestEnvironmoent()
Environment.CurrentProcessLocation.Value,
new[] { TestModConfigA.ModId, TestModConfigB.ModId, TestModConfigD.ModId });

ConfigurationPathOfThisApp = Path.Combine(TestConfig.ApplicationConfigDirectory, IdOfThisApp, ApplicationConfig.ConfigFileName);
ConfigurationPathOfThisApp = Path.Combine(TestConfig.GetApplicationConfigDirectory(), IdOfThisApp, ApplicationConfig.ConfigFileName);
IConfig<ApplicationConfig>.ToPath(ThisApplication, ConfigurationPathOfThisApp);

// Populate nonexisting dependencies.
Expand All @@ -131,10 +131,10 @@ public void Dispose()
public static LoaderConfig MakeTestConfig()
{
var config = new LoaderConfig();
config.ApplicationConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.ApplicationConfigDirectory, "Apps");
config.ModConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.ModConfigDirectory, "Mods");
config.PluginConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.PluginConfigDirectory, "Plugins");
config.ModUserConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.ModUserConfigDirectory, "UserConfigs");
config.ApplicationConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.GetApplicationConfigDirectory(), "Apps");
config.ModConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.GetModConfigDirectory(), "Mods");
config.PluginConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.GetPluginConfigDirectory(), "Plugins");
config.ModUserConfigDirectory = IfNotExistsMakeDefaultDirectoryAndReturnFullPath(config.GetModUserConfigDirectory(), "UserConfigs");
config.EnabledPlugins = EmptyArray<string>.Instance;
return config;
}
Expand Down
2 changes: 1 addition & 1 deletion source/Reloaded.Mod.Loader.Tests/Update/UpdaterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public UpdaterTests()
Task.Run(() => extractor.ExtractPackageAsync(TestArchiveFile, newPackageFolder.FolderPath)).GetAwaiter().GetResult();
Task.Run(() => extractor.ExtractPackageAsync(TestArchiveFileOld, oldPackageFolder.FolderPath)).GetAwaiter().GetResult();

var targetModDirectory = Path.Combine(_testEnvironmoent.TestConfig.ModConfigDirectory, "OldPackage");
var targetModDirectory = Path.Combine(_testEnvironmoent.TestConfig.GetModConfigDirectory(), "OldPackage");
IOEx.CopyDirectory(oldPackageFolder.FolderPath, targetModDirectory);
_foldersToDelete.Add(targetModDirectory);

Expand Down

0 comments on commit f701528

Please sign in to comment.