From 3df9efb95d4b402c0406499aeb7dd83b6584dd69 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 5 Aug 2016 17:07:04 +0100 Subject: [PATCH] Rework mod enumeration / caching. - Replaced ModMetadata.AllMods with Game.Mods. - Store / reference mod Manifest instead of ModMetadata. - Removes engine dependency on ModContent class. --- OpenRA.Game/FileSystem/FileSystem.cs | 13 +- OpenRA.Game/Game.cs | 20 +-- OpenRA.Game/IUtilityCommand.cs | 4 +- OpenRA.Game/InstalledMods.cs | 117 ++++++++++++++++++ OpenRA.Game/Manifest.cs | 50 +++++++- OpenRA.Game/ModData.cs | 10 +- OpenRA.Game/ModMetadata.cs | 104 ---------------- OpenRA.Game/Network/GameServer.cs | 8 +- OpenRA.Game/Network/ReplayRecorder.cs | 4 +- OpenRA.Game/Network/UnitOrders.cs | 10 +- OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Server/Server.cs | 6 +- OpenRA.Game/World.cs | 2 +- .../LoadScreens/BlankLoadScreen.cs | 2 +- .../ServerTraits/MasterServerPinger.cs | 4 +- .../UtilityCommands/ImportLegacyMapCommand.cs | 2 +- .../ListInstallShieldContentsCommand.cs | 3 +- .../UtilityCommands/ListMixContentsCommand.cs | 2 +- .../UtilityCommands/RemapShpCommand.cs | 4 +- .../UtilityCommands/UpgradeRules.cs | 6 +- .../Widgets/Logic/Editor/SaveMapLogic.cs | 2 +- .../Logic/Installation/InstallModLogic.cs | 2 +- .../Logic/Installation/ModContentLogic.cs | 6 +- .../Installation/ModContentPromptLogic.cs | 8 +- .../Widgets/Logic/MainMenuLogic.cs | 6 +- .../Widgets/Logic/ModBrowserLogic.cs | 62 ++++++---- .../Widgets/Logic/MultiplayerLogic.cs | 6 +- .../Widgets/Logic/ReplayBrowserLogic.cs | 4 +- .../Widgets/Logic/ReplayUtils.cs | 6 +- .../UtilityCommands/ImportD2kMapCommand.cs | 2 +- .../UtilityCommands/ImportTSMapCommand.cs | 2 +- OpenRA.Server/Program.cs | 6 +- OpenRA.Utility/Program.cs | 36 +++--- 33 files changed, 301 insertions(+), 219 deletions(-) create mode 100644 OpenRA.Game/InstalledMods.cs diff --git a/OpenRA.Game/FileSystem/FileSystem.cs b/OpenRA.Game/FileSystem/FileSystem.cs index e21b89fe7c..692a55fa82 100644 --- a/OpenRA.Game/FileSystem/FileSystem.cs +++ b/OpenRA.Game/FileSystem/FileSystem.cs @@ -13,7 +13,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using OpenRA.Primitives; namespace OpenRA.FileSystem @@ -34,9 +33,15 @@ namespace OpenRA.FileSystem // Mod packages that should not be disposed readonly List modPackages = new List(); + readonly IReadOnlyDictionary installedMods; Cache> fileIndex = new Cache>(_ => new List()); + public FileSystem(IReadOnlyDictionary installedMods) + { + this.installedMods = installedMods; + } + public IReadOnlyPackage OpenPackage(string filename) { if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase)) @@ -108,9 +113,9 @@ namespace OpenRA.FileSystem { name = name.Substring(1); - ModMetadata mod; - if (!ModMetadata.AllMods.TryGetValue(name, out mod)) - throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, ModMetadata.AllMods.Keys.JoinWith(", "))); + Manifest mod; + if (!installedMods.TryGetValue(name, out mod)) + throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", "))); package = mod.Package; modPackages.Add(package); diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 549cf03e1c..6ef06af877 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -21,6 +21,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using OpenRA.Chat; +using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Primitives; @@ -35,6 +36,8 @@ namespace OpenRA public const int Timestep = 40; public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms + public static InstalledMods Mods { get; private set; } + public static ModData ModData; public static Settings Settings; public static ICursor Cursor; @@ -300,22 +303,23 @@ namespace OpenRA GlobalChat = new GlobalChat(); + Mods = new InstalledMods(); Console.WriteLine("Available mods:"); - foreach (var mod in ModMetadata.AllMods) - Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version); + foreach (var mod in Mods) + Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Mod.Title, mod.Value.Mod.Version); InitializeMod(Settings.Game.Mod, args); } public static bool IsModInstalled(string modId) { - return ModMetadata.AllMods[modId].RequiresMods.All(IsModInstalled); + return Mods.ContainsKey(modId) && Mods[modId].RequiresMods.All(IsModInstalled); } public static bool IsModInstalled(KeyValuePair mod) { - return ModMetadata.AllMods.ContainsKey(mod.Key) - && ModMetadata.AllMods[mod.Key].Version == mod.Value + return Mods.ContainsKey(mod.Key) + && Mods[mod.Key].Mod.Version == mod.Value && IsModInstalled(mod.Key); } @@ -347,7 +351,7 @@ namespace OpenRA ModData = null; // Fall back to default if the mod doesn't exist or has missing prerequisites. - if (!ModMetadata.AllMods.ContainsKey(mod) || !IsModInstalled(mod)) + if (!IsModInstalled(mod)) mod = new GameSettings().Mod; Console.WriteLine("Loading mod: {0}", mod); @@ -355,7 +359,7 @@ namespace OpenRA Sound.StopVideo(); - ModData = new ModData(mod, true); + ModData = new ModData(Mods[mod], Mods, true); using (new PerfTimer("LoadMaps")) ModData.MapCache.LoadMaps(); @@ -468,7 +472,7 @@ namespace OpenRA ThreadPool.QueueUserWorkItem(_ => { var mod = ModData.Manifest.Mod; - var directory = Platform.ResolvePath("^", "Screenshots", mod.Id, mod.Version); + var directory = Platform.ResolvePath("^", "Screenshots", ModData.Manifest.Id, mod.Version); Directory.CreateDirectory(directory); var filename = TimestampedFilename(); diff --git a/OpenRA.Game/IUtilityCommand.cs b/OpenRA.Game/IUtilityCommand.cs index 7a5fb755b0..ff7642acc0 100644 --- a/OpenRA.Game/IUtilityCommand.cs +++ b/OpenRA.Game/IUtilityCommand.cs @@ -16,10 +16,12 @@ namespace OpenRA public class Utility { public readonly ModData ModData; + public readonly InstalledMods Mods; - public Utility(ModData modData) + public Utility(ModData modData, InstalledMods mods) { ModData = modData; + Mods = mods; } } diff --git a/OpenRA.Game/InstalledMods.cs b/OpenRA.Game/InstalledMods.cs new file mode 100644 index 0000000000..2fcbc6d5bb --- /dev/null +++ b/OpenRA.Game/InstalledMods.cs @@ -0,0 +1,117 @@ +#region Copyright & License Information +/* + * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using OpenRA.FileSystem; +using OpenRA.Primitives; + +namespace OpenRA +{ + public class InstalledMods : IReadOnlyDictionary + { + readonly Dictionary mods; + + public InstalledMods() + { + mods = GetInstalledMods(); + } + + static IEnumerable> GetCandidateMods() + { + // 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(); + + 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)); + + return mods; + } + + static Manifest LoadMod(string id, string path) + { + IReadOnlyPackage package = null; + try + { + if (Directory.Exists(path)) + package = new Folder(path); + else + { + try + { + using (var fileStream = File.OpenRead(path)) + package = new ZipFile(fileStream, path); + } + catch + { + throw new InvalidDataException(path + " is not a valid mod package"); + } + } + + if (!package.Contains("mod.yaml")) + throw new InvalidDataException(path + " is not a valid mod package"); + + // Mods in the support directory and oramod packages (which are listed later + // in the CandidateMods list) override mods in the main install. + return new Manifest(id, package); + } + catch (Exception) + { + if (package != null) + package.Dispose(); + + return null; + } + } + + static Dictionary GetInstalledMods() + { + var ret = new Dictionary(); + foreach (var pair in GetCandidateMods()) + { + var mod = LoadMod(pair.First, pair.Second); + + // Mods in the support directory and oramod packages (which are listed later + // in the CandidateMods list) override mods in the main install. + if (mod != null) + ret[pair.First] = mod; + } + + return ret; + } + + public Manifest this[string key] { get { return mods[key]; } } + public int Count { get { return mods.Count; } } + public ICollection Keys { get { return mods.Keys; } } + public ICollection Values { get { return mods.Values; } } + public bool ContainsKey(string key) { return mods.ContainsKey(key); } + public IEnumerator> GetEnumerator() { return mods.GetEnumerator(); } + public bool TryGetValue(string key, out Manifest value) { return mods.TryGetValue(key, out value); } + IEnumerator IEnumerable.GetEnumerator() { return mods.GetEnumerator(); } + } +} \ No newline at end of file diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index ed9bd7e63a..6bcc24d924 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -34,6 +34,8 @@ namespace OpenRA /// Describes what is to be loaded in order to run a mod. public class Manifest { + public readonly string Id; + public readonly IReadOnlyPackage Package; public readonly ModMetadata Mod; public readonly string[] Rules, ServerTraits, @@ -60,14 +62,15 @@ namespace OpenRA readonly TypeDictionary modules = new TypeDictionary(); readonly Dictionary yaml; - public Manifest(string modId) - { - var package = ModMetadata.AllMods[modId].Package; + bool customDataLoaded; + public Manifest(string modId, IReadOnlyPackage package) + { + Id = modId; + Package = package; yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary(); Mod = FieldLoader.Load(yaml["Metadata"]); - Mod.Id = modId; // TODO: Use fieldloader MapFolders = YamlDictionary(yaml, "MapFolders"); @@ -106,7 +109,7 @@ namespace OpenRA RequiresMods = yaml["RequiresMods"].ToDictionary(my => my.Value); // Allow inherited mods to import parent maps. - var compat = new List { Mod.Id }; + var compat = new List { Id }; if (yaml.ContainsKey("SupportsMapsFrom")) compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim())); @@ -147,6 +150,8 @@ namespace OpenRA modules.Add(module); } + + customDataLoaded = true; } static string[] YamlList(Dictionary yaml, string key, bool parsePaths = false) @@ -171,8 +176,12 @@ namespace OpenRA return modules.Contains(); } + /// Load a cached IGlobalModData instance. public T Get() where T : IGlobalModData { + if (!customDataLoaded) + throw new InvalidOperationException("Attempted to call Manifest.Get() before loading custom data!"); + var module = modules.GetOrDefault(); // Lazily create the default values if not explicitly defined. @@ -184,5 +193,36 @@ namespace OpenRA return module; } + + /// + /// Load an uncached IGlobalModData instance directly from the manifest yaml. + /// This should only be used by external mods that want to query data from this mod. + /// + public T Get(ObjectCreator oc) where T : IGlobalModData + { + MiniYaml data; + var t = typeof(T); + if (!yaml.TryGetValue(t.Name, out data)) + { + // Lazily create the default values if not explicitly defined. + return (T)oc.CreateBasic(t); + } + + IGlobalModData module; + var ctor = t.GetConstructor(new[] { typeof(MiniYaml) }); + if (ctor != null) + { + // Class has opted-in to DIY initialization + module = (IGlobalModData)ctor.Invoke(new object[] { data.Value }); + } + else + { + // Automatically load the child nodes using FieldLoader + module = oc.CreateObject(t.Name); + FieldLoader.Load(module, data); + } + + return (T)module; + } } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index e1a0cc3497..51a88b4a3e 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -33,7 +33,7 @@ namespace OpenRA public ILoadScreen LoadScreen { get; private set; } public VoxelLoader VoxelLoader { get; private set; } public CursorProvider CursorProvider { get; private set; } - public FS ModFiles = new FS(); + public FS ModFiles; public IReadOnlyFileSystem DefaultFileSystem { get { return ModFiles; } } readonly Lazy defaultRules; @@ -45,10 +45,14 @@ namespace OpenRA readonly Lazy> defaultSequences; public IReadOnlyDictionary DefaultSequences { get { return defaultSequences.Value; } } - public ModData(string mod, bool useLoadScreen = false) + public ModData(Manifest mod, InstalledMods mods, bool useLoadScreen = false) { Languages = new string[0]; - Manifest = new Manifest(mod); + + ModFiles = new FS(mods); + + // Take a local copy of the manifest + Manifest = new Manifest(mod.Id, mod.Package); ModFiles.LoadFromManifest(Manifest); ObjectCreator = new ObjectCreator(Manifest, ModFiles); diff --git a/OpenRA.Game/ModMetadata.cs b/OpenRA.Game/ModMetadata.cs index 788794dbb6..f8e039f79a 100644 --- a/OpenRA.Game/ModMetadata.cs +++ b/OpenRA.Game/ModMetadata.cs @@ -9,118 +9,14 @@ */ #endregion -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using OpenRA.FileSystem; -using OpenRA.Primitives; - namespace OpenRA { public class ModMetadata { - public static readonly Dictionary AllMods = ValidateMods(); - - public string Id; public string Title; public string Description; public string Version; public string Author; public bool Hidden; - - public Dictionary RequiresMods; - public ModContent ModContent; - public IReadOnlyPackage Package; - - static Dictionary ValidateMods() - { - var ret = new Dictionary(); - foreach (var pair in GetCandidateMods()) - { - IReadOnlyPackage package = null; - try - { - if (Directory.Exists(pair.Second)) - package = new Folder(pair.Second); - else - { - try - { - using (var fileStream = File.OpenRead(pair.Second)) - package = new ZipFile(fileStream, pair.Second); - } - catch - { - throw new InvalidDataException(pair.Second + " is not a valid mod package"); - } - } - - if (!package.Contains("mod.yaml")) - { - package.Dispose(); - continue; - } - - var yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")); - var nd = yaml.ToDictionary(); - if (!nd.ContainsKey("Metadata")) - { - package.Dispose(); - continue; - } - - var metadata = FieldLoader.Load(nd["Metadata"]); - metadata.Id = pair.First; - metadata.Package = package; - - if (nd.ContainsKey("RequiresMods")) - metadata.RequiresMods = nd["RequiresMods"].ToDictionary(my => my.Value); - else - metadata.RequiresMods = new Dictionary(); - - if (nd.ContainsKey("ModContent")) - metadata.ModContent = FieldLoader.Load(nd["ModContent"]); - - // Mods in the support directory and oramod packages (which are listed later - // in the CandidateMods list) override mods in the main install. - ret[pair.First] = metadata; - } - catch (Exception ex) - { - if (package != null) - package.Dispose(); - Console.WriteLine("An exception occurred when trying to load ModMetadata for `{0}`:".F(pair.First)); - Console.WriteLine(ex.Message); - } - } - - return ret; - } - - static IEnumerable> GetCandidateMods() - { - // 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(); - - 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)); - - return mods; - } } } diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs index af3976c119..5110aecd52 100644 --- a/OpenRA.Game/Network/GameServer.cs +++ b/OpenRA.Game/Network/GameServer.cs @@ -38,14 +38,14 @@ namespace OpenRA.Network { FieldLoader.Load(this, yaml); - ModMetadata mod; + Manifest mod; var modVersion = Mods.Split('@'); - if (modVersion.Length == 2 && ModMetadata.AllMods.TryGetValue(modVersion[0], out mod)) + if (modVersion.Length == 2 && Game.Mods.TryGetValue(modVersion[0], out mod)) { ModId = modVersion[0]; ModVersion = modVersion[1]; - ModLabel = "{0} ({1})".F(mod.Title, modVersion[1]); - IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Version; + ModLabel = "{0} ({1})".F(mod.Mod.Title, modVersion[1]); + IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Mod.Version; } else ModLabel = "Unknown mod: {0}".F(Mods); diff --git a/OpenRA.Game/Network/ReplayRecorder.cs b/OpenRA.Game/Network/ReplayRecorder.cs index d4d9572542..cf694694cc 100644 --- a/OpenRA.Game/Network/ReplayRecorder.cs +++ b/OpenRA.Game/Network/ReplayRecorder.cs @@ -44,8 +44,8 @@ namespace OpenRA.Network void StartSavingReplay(byte[] initialContent) { var filename = chooseFilename(); - var mod = Game.ModData.Manifest.Mod; - var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Version); + var mod = Game.ModData.Manifest; + var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Mod.Version); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 790a6a4838..5301d9ec61 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -132,13 +132,13 @@ namespace OpenRA.Network case "HandshakeRequest": { // Switch to the server's mod if we need and are able to - var mod = Game.ModData.Manifest.Mod; + var mod = Game.ModData.Manifest; var request = HandshakeRequest.Deserialize(order.TargetString); - ModMetadata serverMod; + Manifest serverMod; if (request.Mod != mod.Id && - ModMetadata.AllMods.TryGetValue(request.Mod, out serverMod) && - serverMod.Version == request.Version) + Game.Mods.TryGetValue(request.Mod, out serverMod) && + serverMod.Mod.Version == request.Version) { var replay = orderManager.Connection as ReplayConnection; var launchCommand = replay != null ? @@ -170,7 +170,7 @@ namespace OpenRA.Network { Client = info, Mod = mod.Id, - Version = mod.Version, + Version = mod.Mod.Version, Password = orderManager.Password }; diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index cef048ee0f..399078bb9e 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -245,6 +245,7 @@ + diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index f78c0d34d9..479c7421a8 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -158,7 +158,7 @@ namespace OpenRA.Server foreach (var t in serverTraits.WithInterface()) t.ServerStarted(this); - Log.Write("server", "Initial mod: {0}", ModData.Manifest.Mod.Id); + Log.Write("server", "Initial mod: {0}", ModData.Manifest.Id); Log.Write("server", "Initial map: {0}", LobbyInfo.GlobalSettings.Map); var timeout = serverTraits.WithInterface().Min(t => t.TickTimeout); @@ -262,7 +262,7 @@ namespace OpenRA.Server // Dispatch a handshake order var request = new HandshakeRequest { - Mod = ModData.Manifest.Mod.Id, + Mod = ModData.Manifest.Id, Version = ModData.Manifest.Mod.Version, Map = LobbyInfo.GlobalSettings.Map }; @@ -327,7 +327,7 @@ namespace OpenRA.Server else client.Color = HSLColor.FromRGB(255, 255, 255); - if (ModData.Manifest.Mod.Id != handshake.Mod) + if (ModData.Manifest.Id != handshake.Mod) { Log.Write("server", "Rejected connection from {0}; mods do not match.", newConn.Socket.RemoteEndPoint); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 192509e588..d0967b0d7d 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -176,7 +176,7 @@ namespace OpenRA gameInfo = new GameInformation { - Mod = Game.ModData.Manifest.Mod.Id, + Mod = Game.ModData.Manifest.Id, Version = Game.ModData.Manifest.Mod.Version, MapUid = Map.Uid, diff --git a/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs b/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs index 6e17206346..421ea380fc 100644 --- a/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs +++ b/OpenRA.Mods.Common/LoadScreens/BlankLoadScreen.cs @@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.LoadScreens if (replayMeta != null) { var mod = replayMeta.GameInfo.Mod; - if (mod != null && mod != Game.ModData.Manifest.Mod.Id && ModMetadata.AllMods.ContainsKey(mod)) + if (mod != null && mod != Game.ModData.Manifest.Id && Game.Mods.ContainsKey(mod)) Game.InitializeMod(mod, args); } diff --git a/OpenRA.Mods.Common/ServerTraits/MasterServerPinger.cs b/OpenRA.Mods.Common/ServerTraits/MasterServerPinger.cs index f8b2745521..2e555b9151 100644 --- a/OpenRA.Mods.Common/ServerTraits/MasterServerPinger.cs +++ b/OpenRA.Mods.Common/ServerTraits/MasterServerPinger.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Server lastPing = Game.RunTime; isBusy = true; - var mod = server.ModData.Manifest.Mod; + var mod = server.ModData.Manifest; // important to grab these on the main server thread, not in the worker we're about to spawn -- they may be modified // by the main thread as clients join and leave. @@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Server (int)server.State, numPlayers, numBots, - "{0}@{1}".F(mod.Id, mod.Version), + "{0}@{1}".F(mod.Id, mod.Mod.Version), server.LobbyInfo.GlobalSettings.Map, numSlots, numSpectators, diff --git a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs index 61ed796d1e..fabf462fad 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs @@ -74,7 +74,7 @@ namespace OpenRA.Mods.Common.UtilityCommands Author = "Westwood Studios", }; - Map.RequiresMod = ModData.Manifest.Mod.Id; + Map.RequiresMod = ModData.Manifest.Id; SetBounds(Map, mapSection); diff --git a/OpenRA.Mods.Common/UtilityCommands/ListInstallShieldContentsCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ListInstallShieldContentsCommand.cs index ee23eb7e2b..53eab686ea 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ListInstallShieldContentsCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ListInstallShieldContentsCommand.cs @@ -11,7 +11,6 @@ using System; using System.IO; -using System.Linq; using OpenRA.FileSystem; namespace OpenRA.Mods.Common.UtilityCommands @@ -31,7 +30,7 @@ namespace OpenRA.Mods.Common.UtilityCommands var filename = Path.GetFileName(args[1]); var path = Path.GetDirectoryName(args[1]); - var fs = new OpenRA.FileSystem.FileSystem(); + var fs = new FileSystem.FileSystem(utility.Mods); fs.Mount(path, "parent"); var package = new InstallShieldPackage(fs, "parent|" + filename); diff --git a/OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs index b26f90f73b..a5967f9394 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.UtilityCommands var filename = Path.GetFileName(args[1]); var path = Path.GetDirectoryName(args[1]); - var fs = new OpenRA.FileSystem.FileSystem(); + var fs = new FileSystem.FileSystem(utility.Mods); // Needed to access the global mix database fs.LoadFromManifest(utility.ModData.Manifest); diff --git a/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs index 6e178721c1..04267f9270 100644 --- a/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs @@ -39,14 +39,14 @@ namespace OpenRA.Mods.Common.UtilityCommands remap[i] = i; var srcMod = args[1].Split(':')[0]; - var srcModData = new ModData(srcMod); + var srcModData = new ModData(utility.Mods[srcMod], utility.Mods); Game.ModData = srcModData; var srcPaletteInfo = srcModData.DefaultRules.Actors["player"].TraitInfo(); var srcRemapIndex = srcPaletteInfo.RemapIndex; var destMod = args[2].Split(':')[0]; - var destModData = new ModData(destMod); + var destModData = new ModData(utility.Mods[destMod], utility.Mods); Game.ModData = destModData; var destPaletteInfo = destModData.DefaultRules.Actors["player"].TraitInfo(); var destRemapIndex = destPaletteInfo.RemapIndex; diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index a69e512444..f764b176da 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -346,7 +346,7 @@ namespace OpenRA.Mods.Common.UtilityCommands { foreach (var node in nodes) { - if (engineVersion < 20160730 && modData.Manifest.Mod.Id == "d2k" && depth == 2) + if (engineVersion < 20160730 && modData.Manifest.Id == "d2k" && depth == 2) { if (node.Key == "Start") node.Value.Value = RemapD2k106Sequence(FieldLoader.GetValue("", node.Value.Value)).ToString(); @@ -416,7 +416,7 @@ namespace OpenRA.Mods.Common.UtilityCommands foreach (var node in nodes) { // Fix RA building footprints to not use _ when it's not necessary - if (engineVersion < 20160619 && modData.Manifest.Mod.Id == "ra" && depth == 1) + if (engineVersion < 20160619 && modData.Manifest.Id == "ra" && depth == 1) { var buildings = new List() { "tsla", "gap", "agun", "apwr", "fapw" }; if (buildings.Contains(parent.Value.Value) && node.Key == "Location") @@ -424,7 +424,7 @@ namespace OpenRA.Mods.Common.UtilityCommands } // Fix TD building footprints to not use _ when it's not necessary - if (engineVersion < 20160619 && modData.Manifest.Mod.Id == "cnc" && depth == 1) + if (engineVersion < 20160619 && modData.Manifest.Id == "cnc" && depth == 1) { var buildings = new List() { "atwr", "obli", "tmpl", "weap", "hand" }; if (buildings.Contains(parent.Value.Value) && node.Key == "Location") diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs index 436bbfcadf..e9f1d9a337 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs @@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (playerDefinitions != null) map.PlayerDefinitions = playerDefinitions; - map.RequiresMod = modData.Manifest.Mod.Id; + map.RequiresMod = modData.Manifest.Id; var combinedPath = Platform.ResolvePath(Path.Combine(selectedDirectory.Folder.Name, filename.Text + fileTypes[fileType].Extension)); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallModLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallModLogic.cs index 07968a2651..85324c63cb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallModLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallModLogic.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var panel = widget.Get("INSTALL_MOD_PANEL"); - var mods = ModMetadata.AllMods[modId].RequiresMods.Where(m => !Game.IsModInstalled(m)).Select(m => "{0} ({1})".F(m.Key, m.Value)); + var mods = Game.Mods[modId].RequiresMods.Where(m => !Game.IsModInstalled(m)).Select(m => "{0} ({1})".F(m.Key, m.Value)); var text = string.Join(", ", mods); panel.Get("MOD_LIST").Text = text; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs index 32ca8134e8..245ee0eac4 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.IO; using System.Linq; using OpenRA.Widgets; @@ -24,11 +23,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic bool discAvailable; [ObjectCreator.UseCtor] - public ModContentLogic(Widget widget, string modId, Action onCancel) + public ModContentLogic(Widget widget, Manifest mod, ModContent content, Action onCancel) { + this.content = content; + var panel = widget.Get("CONTENT_PANEL"); - content = ModMetadata.AllMods[modId].ModContent; scrollPanel = panel.Get("PACKAGES"); template = scrollPanel.Get("PACKAGE_TEMPLATE"); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentPromptLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentPromptLogic.cs index 63d41c0e15..eb2a34a4ae 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentPromptLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentPromptLogic.cs @@ -17,13 +17,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic public class ModContentPromptLogic : ChromeLogic { [ObjectCreator.UseCtor] - public ModContentPromptLogic(Widget widget, string modId, Action continueLoading) + public ModContentPromptLogic(Widget widget, Manifest mod, ModContent content, Action continueLoading) { var panel = widget.Get("CONTENT_PROMPT_PANEL"); - var mod = ModMetadata.AllMods[modId]; - var content = ModMetadata.AllMods[modId].ModContent; - var headerTemplate = panel.Get("HEADER_TEMPLATE"); var headerLines = !string.IsNullOrEmpty(content.InstallPromptMessage) ? content.InstallPromptMessage.Replace("\\n", "\n").Split('\n') : new string[0]; var headerHeight = 0; @@ -46,7 +43,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic { Ui.OpenWindow("CONTENT_PANEL", new WidgetArgs { - { "modId", modId }, + { "mod", mod }, + { "content", content }, { "onCancel", Ui.CloseWindow } }); }; diff --git a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs index 04f1148c9e..71960401ae 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs @@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic // so we can't do this inside the input handler. Game.RunAfterTick(() => { - Game.Settings.Game.PreviousMod = modData.Manifest.Mod.Id; + Game.Settings.Game.PreviousMod = modData.Manifest.Id; Game.InitializeMod("modchooser", null); }); }; @@ -283,8 +283,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic { // Send the mod and engine version to support version-filtered news (update prompts) var newsURL = Game.Settings.Game.NewsUrl + "?version={0}&mod={1}&modversion={2}".F( - Uri.EscapeUriString(ModMetadata.AllMods["modchooser"].Version), - Uri.EscapeUriString(Game.ModData.Manifest.Mod.Id), + Uri.EscapeUriString(Game.Mods["modchooser"].Mod.Version), + Uri.EscapeUriString(Game.ModData.Manifest.Id), Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version)); // Append system profile data if the player has opted in diff --git a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs index 7a74dbd966..024c1f8224 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs @@ -24,32 +24,39 @@ namespace OpenRA.Mods.Common.Widgets.Logic { readonly Widget modList; readonly ButtonWidget modTemplate; - readonly ModMetadata[] allMods; + readonly Manifest[] allMods; readonly Dictionary previews = new Dictionary(); readonly Dictionary logos = new Dictionary(); + readonly Cache content = new Cache(LoadModContent); + readonly Widget modChooserPanel; readonly ButtonWidget loadButton; readonly SheetBuilder sheetBuilder; - ModMetadata selectedMod; + Manifest selectedMod; string selectedAuthor; string selectedDescription; int modOffset = 0; + static ModContent LoadModContent(Manifest mod) + { + return mod.Get(Game.ModData.ObjectCreator); + } + [ObjectCreator.UseCtor] public ModBrowserLogic(Widget widget, ModData modData) { modChooserPanel = widget; loadButton = modChooserPanel.Get("LOAD_BUTTON"); loadButton.OnClick = () => LoadMod(selectedMod); - loadButton.IsDisabled = () => selectedMod.Id == modData.Manifest.Mod.Id; + loadButton.IsDisabled = () => selectedMod.Id == modData.Manifest.Id; var contentButton = modChooserPanel.Get("CONFIGURE_BUTTON"); - contentButton.IsDisabled = () => selectedMod.ModContent == null; contentButton.OnClick = () => { var widgetArgs = new WidgetArgs { - { "modId", selectedMod.Id }, + { "mod", selectedMod }, + { "content", content[selectedMod] }, { "onCancel", () => { } } }; @@ -62,9 +69,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic modTemplate = modList.Get("MOD_TEMPLATE"); modChooserPanel.Get("MOD_DESC").GetText = () => selectedDescription; - modChooserPanel.Get("MOD_TITLE").GetText = () => selectedMod.Title; + modChooserPanel.Get("MOD_TITLE").GetText = () => selectedMod.Mod.Title; modChooserPanel.Get("MOD_AUTHOR").GetText = () => selectedAuthor; - modChooserPanel.Get("MOD_VERSION").GetText = () => selectedMod.Version; + modChooserPanel.Get("MOD_VERSION").GetText = () => selectedMod.Mod.Version; var prevMod = modChooserPanel.Get("PREV_MOD"); prevMod.OnClick = () => { modOffset -= 1; RebuildModList(); }; @@ -82,8 +89,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; sheetBuilder = new SheetBuilder(SheetType.BGRA); - allMods = ModMetadata.AllMods.Values.Where(m => !m.Hidden) - .OrderBy(m => m.Title) + allMods = Game.Mods.Values.Where(m => !m.Mod.Hidden) + .OrderBy(m => m.Mod.Title) .ToArray(); // Load preview images, and eat any errors @@ -91,7 +98,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { try { - using (var stream = ModMetadata.AllMods[mod.Id].Package.GetStream("preview.png")) + using (var stream = mod.Package.GetStream("preview.png")) using (var preview = new Bitmap(stream)) if (preview.Width == 296 && preview.Height == 196) previews.Add(mod.Id, sheetBuilder.Add(preview)); @@ -100,7 +107,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic try { - using (var stream = ModMetadata.AllMods[mod.Id].Package.GetStream("logo.png")) + using (var stream = mod.Package.GetStream("logo.png")) using (var logo = new Bitmap(stream)) if (logo.Width == 96 && logo.Height == 96) logos.Add(mod.Id, sheetBuilder.Add(logo)); @@ -108,9 +115,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic catch (Exception) { } } - ModMetadata initialMod; - ModMetadata.AllMods.TryGetValue(Game.Settings.Game.PreviousMod, out initialMod); - SelectMod(initialMod != null && initialMod.Id != "modchooser" ? initialMod : ModMetadata.AllMods["ra"]); + Manifest initialMod; + Game.Mods.TryGetValue(Game.Settings.Game.PreviousMod, out initialMod); + SelectMod(initialMod != null && initialMod.Id != "modchooser" ? initialMod : Game.Mods["ra"]); RebuildModList(); } @@ -146,7 +153,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic SelectMod(mod); }; - item.TooltipText = mod.Title; + item.TooltipText = mod.Mod.Title; if (j < 9) item.Key = new Hotkey((Keycode)((int)Keycode.NUMBER_1 + j), Modifiers.None); @@ -160,23 +167,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } - void SelectMod(ModMetadata mod) + void SelectMod(Manifest mod) { selectedMod = mod; - selectedAuthor = "By " + (mod.Author ?? "unknown author"); - selectedDescription = (mod.Description ?? "").Replace("\\n", "\n"); + selectedAuthor = "By " + (mod.Mod.Author ?? "unknown author"); + selectedDescription = (mod.Mod.Description ?? "").Replace("\\n", "\n"); var selectedIndex = Array.IndexOf(allMods, mod); if (selectedIndex - modOffset > 4) modOffset = selectedIndex - 4; } - void LoadMod(ModMetadata mod) + void LoadMod(Manifest mod) { - if (!Game.IsModInstalled(mod.Id)) + var modId = mod.Id; + if (!Game.IsModInstalled(modId)) { var widgetArgs = new WidgetArgs { - { "modId", mod.Id } + { "mod", selectedMod }, + { "content", content[selectedMod] }, }; Ui.OpenWindow("INSTALL_MOD_PANEL", widgetArgs); @@ -188,8 +197,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic var widgetArgs = new WidgetArgs { { "continueLoading", () => - Game.RunAfterTick(() => Game.InitializeMod(mod.Id, new Arguments())) }, - { "modId", mod.Id } + Game.RunAfterTick(() => Game.InitializeMod(modId, new Arguments())) }, + { "mod", selectedMod }, + { "content", content[selectedMod] }, }; Ui.OpenWindow("CONTENT_PROMPT_PANEL", widgetArgs); @@ -201,13 +211,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic { Ui.CloseWindow(); sheetBuilder.Dispose(); - Game.InitializeMod(mod.Id, null); + Game.InitializeMod(modId, null); }); } - static bool IsModInstalled(ModMetadata mod) + bool IsModInstalled(Manifest mod) { - return mod.ModContent.Packages + return content[mod].Packages .Where(p => p.Value.Required) .All(p => p.Value.TestFiles.All(f => File.Exists(Platform.ResolvePath(f)))); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/MultiplayerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MultiplayerLogic.cs index ba4c9e02a1..1150d526e0 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MultiplayerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MultiplayerLogic.cs @@ -316,8 +316,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; var queryURL = Game.Settings.Server.MasterServer + "games?version={0}&mod={1}&modversion={2}".F( - Uri.EscapeUriString(ModMetadata.AllMods["modchooser"].Version), - Uri.EscapeUriString(Game.ModData.Manifest.Mod.Id), + Uri.EscapeUriString(Game.Mods["modchooser"].Mod.Version), + Uri.EscapeUriString(Game.ModData.Manifest.Id), Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version)); currentQuery = new Download(queryURL, _ => { }, onComplete); @@ -330,7 +330,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic return 0; // Games for the current mod+version are sorted first - if (testEntry.ModId == modData.Manifest.Mod.Id) + if (testEntry.ModId == modData.Manifest.Id) return 2; // Followed by games for different mods that are joinable diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index 3ac585e5ed..2c114d0551 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -59,8 +59,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic replayList = panel.Get("REPLAY_LIST"); var template = panel.Get("REPLAY_TEMPLATE"); - var mod = modData.Manifest.Mod; - var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Version); + var mod = modData.Manifest; + var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Mod.Version); if (Directory.Exists(dir)) ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template)); diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs index 37b7466a99..ed7f0b0588 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayUtils.cs @@ -34,10 +34,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (mod == null) return IncompatibleReplayDialog("unknown mod", mod, onCancel); - var allMods = ModMetadata.AllMods; - if (!allMods.ContainsKey(mod)) + if (!Game.Mods.ContainsKey(mod)) return IncompatibleReplayDialog("unavailable mod", mod, onCancel); - else if (allMods[mod].Version != version) + + if (Game.Mods[mod].Mod.Version != version) return IncompatibleReplayDialog("incompatible version", version, onCancel); if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available) diff --git a/OpenRA.Mods.D2k/UtilityCommands/ImportD2kMapCommand.cs b/OpenRA.Mods.D2k/UtilityCommands/ImportD2kMapCommand.cs index 180880fd0b..11127c14a6 100644 --- a/OpenRA.Mods.D2k/UtilityCommands/ImportD2kMapCommand.cs +++ b/OpenRA.Mods.D2k/UtilityCommands/ImportD2kMapCommand.cs @@ -31,7 +31,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands Game.ModData = utility.ModData; var rules = Ruleset.LoadDefaultsForTileSet(utility.ModData, "ARRAKIS"); - var map = D2kMapImporter.Import(args[1], utility.ModData.Manifest.Mod.Id, args[2], rules); + var map = D2kMapImporter.Import(args[1], utility.ModData.Manifest.Id, args[2], rules); if (map == null) return; diff --git a/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs b/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs index afb012c011..78feabfa80 100644 --- a/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs +++ b/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs @@ -168,7 +168,7 @@ namespace OpenRA.Mods.TS.UtilityCommands Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)), Author = "Westwood Studios", Bounds = new Rectangle(iniBounds[0], iniBounds[1], iniBounds[2], 2 * iniBounds[3] + 2 * iniBounds[1]), - RequiresMod = utility.ModData.Manifest.Mod.Id + RequiresMod = utility.ModData.Manifest.Id }; var fullSize = new int2(iniSize[2], iniSize[3]); diff --git a/OpenRA.Server/Program.cs b/OpenRA.Server/Program.cs index b0e7427d3c..27c89fc0c1 100644 --- a/OpenRA.Server/Program.cs +++ b/OpenRA.Server/Program.cs @@ -30,9 +30,11 @@ namespace OpenRA.Server Game.InitializeSettings(new Arguments(args)); var settings = Game.Settings.Server; - // HACK: The engine code *still* assumes that Game.ModData is set var mod = Game.Settings.Game.Mod; - var modData = Game.ModData = new ModData(mod, false); + var mods = new InstalledMods(); + + // HACK: The engine code *still* assumes that Game.ModData is set + var modData = Game.ModData = new ModData(mods[mod], mods); modData.MapCache.LoadMaps(); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 976d5beb0c..779dafc278 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -16,6 +16,8 @@ using System.Runtime.Serialization; namespace OpenRA { + using UtilityActions = Dictionary, Func>>; + [Serializable] public class NoSuchCommandException : Exception { @@ -37,27 +39,29 @@ namespace OpenRA { static void Main(string[] args) { - if (args.Length == 0) - { - PrintUsage(null); - return; - } - Log.AddChannel("perf", null); Log.AddChannel("debug", null); - var modName = args[0]; - if (!ModMetadata.AllMods.Keys.Contains(modName)) + Game.InitializeSettings(Arguments.Empty); + var mods = new InstalledMods(); + + if (args.Length == 0) { - PrintUsage(null); + PrintUsage(mods, null); return; } - Game.InitializeSettings(Arguments.Empty); - var modData = new ModData(modName); - var utility = new Utility(modData); + var modName = args[0]; + if (!mods.Keys.Contains(modName)) + { + PrintUsage(mods, null); + return; + } + + var modData = new ModData(mods[modName], mods); + var utility = new Utility(modData, mods); args = args.Skip(1).ToArray(); - var actions = new Dictionary, Func>>(); + var actions = new UtilityActions(); foreach (var commandType in modData.ObjectCreator.GetTypesImplementing()) { var command = (IUtilityCommand)Activator.CreateInstance(commandType); @@ -67,7 +71,7 @@ namespace OpenRA if (args.Length == 0) { - PrintUsage(actions); + PrintUsage(mods, actions); return; } @@ -106,10 +110,10 @@ namespace OpenRA } } - static void PrintUsage(IDictionary, Func>> actions) + static void PrintUsage(InstalledMods mods, UtilityActions actions) { Console.WriteLine("Run `OpenRA.Utility.exe [MOD]` to see a list of available commands."); - Console.WriteLine("The available mods are: " + string.Join(", ", ModMetadata.AllMods.Keys)); + Console.WriteLine("The available mods are: " + string.Join(", ", mods.Keys)); Console.WriteLine(); if (actions == null)