From 5552000d69ca0cb8ba21386e82a5c50f2c8e21d9 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 7 Mar 2015 09:29:50 +0000 Subject: [PATCH] Allow mods to define custom manifest metadata. --- OpenRA.Game/Manifest.cs | 43 ++++++++++++++++++++++++++++++++++++++++- OpenRA.Game/ModData.cs | 2 ++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index deaca44e89..094e92265f 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -18,6 +18,7 @@ using OpenRA.Primitives; namespace OpenRA { public enum TileShape { Rectangle, Diamond } + public interface IGlobalModData { } // Describes what is to be loaded in order to run a mod public class Manifest @@ -54,10 +55,19 @@ namespace OpenRA [Desc("Default subcell index used if SubCellInit is absent", "0 - full cell, 1 - first sub-cell")] public readonly int SubCellDefaultIndex = 3; + readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules", + "Sequences", "VoxelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons", + "Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", + "ServerTraits", "LoadScreen", "LobbyDefaults", "ContentInstaller", "Fonts", "TileSize", + "TileShape", "SubCells", "SupportsMapsFrom", "SpriteFormats" }; + + readonly TypeDictionary modules = new TypeDictionary(); + readonly Dictionary yaml; + public Manifest(string mod) { var path = Platform.ResolvePath(".", "mods", mod, "mod.yaml"); - var yaml = new MiniYaml(null, MiniYaml.FromFile(path)).ToDictionary(); + yaml = new MiniYaml(null, MiniYaml.FromFile(path)).ToDictionary(); Mod = FieldLoader.Load(yaml["Metadata"]); Mod.Id = mod; @@ -133,6 +143,23 @@ namespace OpenRA SpriteFormats = FieldLoader.GetValue("SpriteFormats", yaml["SpriteFormats"].Value); } + public void LoadCustomData(ObjectCreator oc) + { + foreach (var kv in yaml) + { + if (reservedModuleNames.Contains(kv.Key)) + continue; + + var t = oc.FindType(kv.Key); + if (t == null || !typeof(IGlobalModData).IsAssignableFrom(t)) + throw new InvalidDataException("`{0}` is not a valid mod manifest entry.".F(kv.Key)); + + var module = oc.CreateObject(kv.Key); + FieldLoader.Load(module, kv.Value); + modules.Add(module); + } + } + static string[] YamlList(Dictionary yaml, string key, bool parsePaths = false) { if (!yaml.ContainsKey(key)) @@ -152,5 +179,19 @@ namespace OpenRA return new ReadOnlyDictionary(inner); } + + public T Get() where T : IGlobalModData + { + var module = modules.GetOrDefault(); + + // Lazily create the default values if not explicitly defined. + if (module == null) + { + module = (T)Game.ModData.ObjectCreator.CreateBasic(typeof(T)); + modules.Add(module); + } + + return module; + } } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 5c9e8d33c7..8cba16ed01 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -38,6 +38,8 @@ namespace OpenRA Languages = new string[0]; Manifest = new Manifest(mod); ObjectCreator = new ObjectCreator(Manifest); + Manifest.LoadCustomData(ObjectCreator); + if (useLoadScreen) { LoadScreen = ObjectCreator.CreateObject(Manifest.LoadScreen.Value);