diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 270ef59653..d0f3c490f8 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -249,10 +249,10 @@ namespace OpenRA // Special case handling of Game.Mod argument: if it matches a real filesystem path // then we use this to override the mod search path, and replace it with the mod id var modArgument = args.GetValue("Game.Mod", null); - string customModPath = null; + var explicitModPaths = new string[0]; if (modArgument != null && (File.Exists(modArgument) || Directory.Exists(modArgument))) { - customModPath = modArgument; + explicitModPaths = new[] { modArgument }; args.ReplaceValue("Game.Mod", Path.GetFileNameWithoutExtension(modArgument)); } @@ -314,7 +314,8 @@ namespace OpenRA GlobalChat = new GlobalChat(); - Mods = new InstalledMods(customModPath); + var modSearchPaths = new[] { Path.Combine(".", "mods"), Path.Combine("^", "mods") }; + Mods = new InstalledMods(modSearchPaths, explicitModPaths); Console.WriteLine("Internal mods:"); foreach (var mod in Mods) Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Metadata.Title, mod.Value.Metadata.Version); diff --git a/OpenRA.Game/InstalledMods.cs b/OpenRA.Game/InstalledMods.cs index 2d191b6509..e255e5c44e 100644 --- a/OpenRA.Game/InstalledMods.cs +++ b/OpenRA.Game/InstalledMods.cs @@ -29,34 +29,35 @@ namespace OpenRA readonly Dictionary icons = new Dictionary(); public readonly IReadOnlyDictionary Icons; - public InstalledMods(string customModPath) + /// Initializes the collection of locally installed mods. + /// Filesystem paths to search for mod packages. + /// Filesystem paths to additional mod packages. + public InstalledMods(IEnumerable searchPaths, IEnumerable explicitPaths) { sheetBuilder = new SheetBuilder(SheetType.BGRA, 256); Icons = new ReadOnlyDictionary(icons); - mods = GetInstalledMods(customModPath); + mods = GetInstalledMods(searchPaths, explicitPaths); } - static IEnumerable> GetCandidateMods() + static IEnumerable> GetCandidateMods(IEnumerable searchPaths) { - // Get mods that are in the game folder. - var basePath = Platform.ResolvePath(Path.Combine(".", "mods")); - var mods = Directory.GetDirectories(basePath) - .Select(x => Pair.New(x.Substring(basePath.Length + 1), x)) - .ToList(); + var mods = new List>(); + foreach (var path in searchPaths) + { + try + { + var directory = new DirectoryInfo(Platform.ResolvePath(path)); + foreach (var subdir in directory.EnumerateDirectories()) + mods.Add(Pair.New(subdir.Name, subdir.FullName)); - foreach (var m in Directory.GetFiles(basePath, "*.oramod")) - mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m)); - - // Get mods that are in the support folder. - var supportPath = Platform.ResolvePath(Path.Combine("^", "mods")); - if (!Directory.Exists(supportPath)) - return mods; - - foreach (var pair in Directory.GetDirectories(supportPath).ToDictionary(x => x.Substring(supportPath.Length + 1))) - mods.Add(Pair.New(pair.Key, pair.Value)); - - foreach (var m in Directory.GetFiles(supportPath, "*.oramod")) - mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m)); + foreach (var file in directory.EnumerateFiles("*.oramod")) + mods.Add(Pair.New(Path.GetFileNameWithoutExtension(file.Name), file.FullName)); + } + catch (Exception e) + { + Console.WriteLine("Failed to enumerate mod search path {0}: {1}", path, e.Message); + } + } return mods; } @@ -102,12 +103,11 @@ namespace OpenRA } } - Dictionary GetInstalledMods(string customModPath) + Dictionary GetInstalledMods(IEnumerable searchPaths, IEnumerable explicitPaths) { var ret = new Dictionary(); - var candidates = GetCandidateMods(); - if (customModPath != null) - candidates = candidates.Append(Pair.New(Path.GetFileNameWithoutExtension(customModPath), customModPath)); + var candidates = GetCandidateMods(searchPaths) + .Concat(explicitPaths.Select(p => Pair.New(Path.GetFileNameWithoutExtension(p), p))); foreach (var pair in candidates) { diff --git a/OpenRA.Server/Program.cs b/OpenRA.Server/Program.cs index 0bdf8fa365..74d76dae6c 100644 --- a/OpenRA.Server/Program.cs +++ b/OpenRA.Server/Program.cs @@ -30,10 +30,10 @@ namespace OpenRA.Server // Special case handling of Game.Mod argument: if it matches a real filesystem path // then we use this to override the mod search path, and replace it with the mod id var modArgument = arguments.GetValue("Game.Mod", null); - string customModPath = null; + var explicitModPaths = new string[0]; if (modArgument != null && (File.Exists(modArgument) || Directory.Exists(modArgument))) { - customModPath = modArgument; + explicitModPaths = new[] { modArgument }; arguments.ReplaceValue("Game.Mod", Path.GetFileNameWithoutExtension(modArgument)); } @@ -43,7 +43,8 @@ namespace OpenRA.Server var settings = Game.Settings.Server; var mod = Game.Settings.Game.Mod; - var mods = new InstalledMods(customModPath); + var modSearchPaths = new[] { Path.Combine(".", "mods"), Path.Combine("^", "mods") }; + var mods = new InstalledMods(modSearchPaths, explicitModPaths); // HACK: The engine code *still* assumes that Game.ModData is set var modData = Game.ModData = new ModData(mods[mod], mods); diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index b554d6e065..447cb65de7 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -47,19 +47,20 @@ namespace OpenRA if (args.Length == 0) { - PrintUsage(new InstalledMods(null), null); + PrintUsage(new InstalledMods(new string[0], new string[0]), null); return; } var modId = args[0]; - string customModPath = null; + var explicitModPaths = new string[0]; if (File.Exists(modId) || Directory.Exists(modId)) { - customModPath = modId; + explicitModPaths = new[] { modId }; modId = Path.GetFileNameWithoutExtension(modId); } - var mods = new InstalledMods(customModPath); + var modSearchPaths = new[] { Path.Combine(".", "mods"), Path.Combine("^", "mods") }; + var mods = new InstalledMods(modSearchPaths, explicitModPaths); if (!mods.Keys.Contains(modId)) { PrintUsage(mods, null);