From 6ad5b9ebc4ea281ad9c50f238e8f7ed1181bb278 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 21 Nov 2020 15:40:57 +0000 Subject: [PATCH] Allow the game root directory to be moved away from the binaries. --- OpenRA.Game/ExternalMods.cs | 4 +- OpenRA.Game/Game.cs | 22 +++++++-- OpenRA.Game/Platform.cs | 47 +++++++++++++++++++ .../UtilityCommands/RegisterModCommand.cs | 2 +- .../CheckRuntimeAssembliesCommand.cs | 2 +- OpenRA.Server/Program.cs | 5 ++ OpenRA.Utility/Program.cs | 4 ++ 7 files changed, 78 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/ExternalMods.cs b/OpenRA.Game/ExternalMods.cs index 01d4aac576..96ad758d75 100644 --- a/OpenRA.Game/ExternalMods.cs +++ b/OpenRA.Game/ExternalMods.cs @@ -121,7 +121,7 @@ namespace OpenRA mods[key] = mod; } - internal void Register(Manifest mod, string launchPath, ModRegistration registration) + internal void Register(Manifest mod, string launchPath, IEnumerable launchArgs, ModRegistration registration) { if (mod.Metadata.Hidden) return; @@ -133,7 +133,7 @@ namespace OpenRA new MiniYamlNode("Version", mod.Metadata.Version), new MiniYamlNode("Title", mod.Metadata.Title), new MiniYamlNode("LaunchPath", launchPath), - new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id) + new MiniYamlNode("LaunchArgs", new[] { "Game.Mod=" + mod.Id }.Concat(launchArgs).JoinWith(", ")) })); using (var stream = mod.Package.GetStream("icon.png")) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index bd2d40d59a..ea065ecb77 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -276,6 +276,10 @@ namespace OpenRA static void Initialize(Arguments args) { + var engineDirArg = args.GetValue("Engine.EngineDir", null); + if (!string.IsNullOrEmpty(engineDirArg)) + Platform.OverrideEngineDir(engineDirArg); + var supportDirArg = args.GetValue("Engine.SupportDir", null); if (!string.IsNullOrEmpty(supportDirArg)) Platform.OverrideSupportDir(supportDirArg); @@ -324,7 +328,7 @@ namespace OpenRA Settings.Game.Platform = p; try { - var rendererPath = Path.Combine(Platform.EngineDir, "OpenRA.Platforms." + p + ".dll"); + var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll"); var assembly = Assembly.LoadFile(rendererPath); var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t)); @@ -367,14 +371,24 @@ namespace OpenRA if (modID != null && Mods.TryGetValue(modID, out _)) { - var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location); + var launchPath = args.GetValue("Engine.LaunchPath", null); + var launchArgs = new List(); // Sanitize input from platform-specific launchers // Process.Start requires paths to not be quoted, even if they contain spaces - if (launchPath.First() == '"' && launchPath.Last() == '"') + if (launchPath != null && launchPath.First() == '"' && launchPath.Last() == '"') launchPath = launchPath.Substring(1, launchPath.Length - 2); - ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User); + if (launchPath == null) + { + // When launching the assembly directly we must propagate the Engine.EngineDir argument if defined + // Platform-specific launchers are expected to manage this internally. + launchPath = Assembly.GetEntryAssembly().Location; + if (!string.IsNullOrEmpty(engineDirArg)) + launchArgs.Add("Engine.EngineDir=\"" + engineDirArg + "\""); + } + + ExternalMods.Register(Mods[modID], launchPath, launchArgs, ModRegistration.User); if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod)) ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User); diff --git a/OpenRA.Game/Platform.cs b/OpenRA.Game/Platform.cs index 0204866353..1de967f2f9 100644 --- a/OpenRA.Game/Platform.cs +++ b/OpenRA.Game/Platform.cs @@ -27,6 +27,9 @@ namespace OpenRA static Lazy currentPlatform = Exts.Lazy(GetCurrentPlatform); + static bool engineDirAccessed; + static string engineDir; + static bool supportDirInitialized; static string systemSupportPath; static string legacyUserSupportPath; @@ -170,6 +173,44 @@ namespace OpenRA } public static string EngineDir + { + get + { + // Engine directory defaults to the location of the binaries, + // unless OverrideGameDir is called during startup. + if (!engineDirAccessed) + engineDir = BinDir; + + engineDirAccessed = true; + return engineDir; + } + } + + /// + /// Specify a custom engine directory that already exists on the filesystem. + /// Cannot be called after Platform.EngineDir has been accessed. + /// + public static void OverrideEngineDir(string path) + { + if (engineDirAccessed) + throw new InvalidOperationException("Attempted to override engine directory after it has already been accessed."); + + // Note: Relative paths are interpreted as being relative to BinDir, not the current working dir. + if (!Path.IsPathRooted(path)) + path = Path.Combine(BinDir, path); + + if (!Directory.Exists(path)) + throw new DirectoryNotFoundException(path); + + if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) && + !path.EndsWith(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal)) + path += Path.DirectorySeparatorChar; + + engineDirAccessed = true; + engineDir = path; + } + + public static string BinDir { get { @@ -194,12 +235,18 @@ namespace OpenRA if (path == "^EngineDir") return EngineDir; + if (path == "^BinDir") + return BinDir; + if (path.StartsWith("^SupportDir|", StringComparison.Ordinal)) path = SupportDir + path.Substring(12); if (path.StartsWith("^EngineDir|", StringComparison.Ordinal)) path = EngineDir + path.Substring(11); + if (path.StartsWith("^BinDir|", StringComparison.Ordinal)) + path = BinDir + path.Substring(8); + return path; } } diff --git a/OpenRA.Game/UtilityCommands/RegisterModCommand.cs b/OpenRA.Game/UtilityCommands/RegisterModCommand.cs index 11fb1e8ac0..0b96c11483 100644 --- a/OpenRA.Game/UtilityCommands/RegisterModCommand.cs +++ b/OpenRA.Game/UtilityCommands/RegisterModCommand.cs @@ -31,7 +31,7 @@ namespace OpenRA.UtilityCommands if (args[2] == "user" || args[2] == "both") type |= ModRegistration.User; - new ExternalMods().Register(utility.ModData.Manifest, args[1], type); + new ExternalMods().Register(utility.ModData.Manifest, args[1], Enumerable.Empty(), type); } } } diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckRuntimeAssembliesCommand.cs b/OpenRA.Mods.Common/UtilityCommands/CheckRuntimeAssembliesCommand.cs index 65e788e1b8..c279e4b9b5 100644 --- a/OpenRA.Mods.Common/UtilityCommands/CheckRuntimeAssembliesCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/CheckRuntimeAssembliesCommand.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.UtilityCommands .ToArray(); // Load the renderer assembly so we can check its dependencies - Assembly.LoadFile(Path.Combine(Platform.EngineDir, "OpenRA.Platforms.Default.dll")); + Assembly.LoadFile(Path.Combine(Platform.BinDir, "OpenRA.Platforms.Default.dll")); var missing = new List(); foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) diff --git a/OpenRA.Server/Program.cs b/OpenRA.Server/Program.cs index 2378972d0f..054b5e6cfa 100644 --- a/OpenRA.Server/Program.cs +++ b/OpenRA.Server/Program.cs @@ -23,6 +23,11 @@ namespace OpenRA.Server static void Main(string[] args) { var arguments = new Arguments(args); + + var engineDirArg = arguments.GetValue("Engine.EngineDir", null); + if (!string.IsNullOrEmpty(engineDirArg)) + Platform.OverrideEngineDir(engineDirArg); + var supportDirArg = arguments.GetValue("Engine.SupportDir", null); if (!string.IsNullOrEmpty(supportDirArg)) Platform.OverrideSupportDir(supportDirArg); diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 96a2bd0ea4..e3cc71745d 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -40,6 +40,10 @@ namespace OpenRA { static void Main(string[] args) { + var engineDir = Environment.GetEnvironmentVariable("ENGINE_DIR"); + if (!string.IsNullOrEmpty(engineDir)) + Platform.OverrideEngineDir(engineDir); + Log.AddChannel("perf", null); Log.AddChannel("debug", null);