From 9748b556dc63d759e07f44c3d75a3660a12bc026 Mon Sep 17 00:00:00 2001 From: Pavel Penev Date: Wed, 9 Sep 2015 17:13:13 +0300 Subject: [PATCH] Add RequiresMods tag to the mod Manifest List required mods and their versions to enable mod dependencies on another mods. Also used for identifying the engine version by `modchooser`'s version. --- OpenRA.Game/Game.cs | 11 +++++-- OpenRA.Game/Manifest.cs | 31 ++++++++++++++++++- .../Widgets/Logic/ModBrowserLogic.cs | 8 +++-- mods/cnc/mod.yaml | 3 ++ mods/d2k/mod.yaml | 3 ++ mods/modchooser/mod.yaml | 2 ++ mods/ra/mod.yaml | 3 ++ mods/ts/mod.yaml | 3 ++ 8 files changed, 59 insertions(+), 5 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index fae6837950..07b6522dd1 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -253,6 +253,13 @@ namespace OpenRA RunAfterDelay(Settings.Server.NatDiscoveryTimeout, UPnP.StoppingNatDiscovery); } + public static bool IsModInstalled(string modId) + { + return Manifest.AllMods[modId].RequiresMods.All(mod => ModMetadata.AllMods.ContainsKey(mod.Key) + && ModMetadata.AllMods[mod.Key].Version == mod.Value + && IsModInstalled(mod.Key)); + } + public static void InitializeMod(string mod, Arguments args) { // Clear static state if we have switched mods @@ -276,8 +283,8 @@ namespace OpenRA ModData.Dispose(); ModData = null; - // Fall back to default if the mod doesn't exist - if (!ModMetadata.AllMods.ContainsKey(mod)) + // Fall back to default if the mod doesn't exist or has missing prerequisites. + if (!ModMetadata.AllMods.ContainsKey(mod) || !IsModInstalled(mod)) mod = new GameSettings().Mod; Console.WriteLine("Loading mod: {0}", mod); diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index cba7be926e..d35432d9a8 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -32,6 +32,8 @@ namespace OpenRA // Describes what is to be loaded in order to run a mod public class Manifest { + public static readonly Dictionary AllMods = LoadMods(); + public readonly ModMetadata Mod; public readonly string[] Folders, Rules, ServerTraits, @@ -44,6 +46,7 @@ namespace OpenRA public readonly MiniYaml LoadScreen; public readonly MiniYaml LobbyDefaults; + public readonly Dictionary RequiresMods; public readonly Dictionary> Fonts; public readonly string[] SpriteFormats = { }; @@ -51,7 +54,7 @@ namespace OpenRA 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", "Fonts", "SupportsMapsFrom", "SpriteFormats" }; + "ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "SupportsMapsFrom", "SpriteFormats", "RequiresMods" }; readonly TypeDictionary modules = new TypeDictionary(); readonly Dictionary yaml; @@ -98,6 +101,8 @@ namespace OpenRA return Pair.New(nd["Font"].Value, Exts.ParseIntegerInvariant(nd["Size"].Value)); }); + RequiresMods = yaml["RequiresMods"].ToDictionary(my => my.Value); + // Allow inherited mods to import parent maps. var compat = new List(); compat.Add(mod); @@ -174,5 +179,29 @@ namespace OpenRA return module; } + + static Dictionary LoadMods() + { + var basePath = Platform.ResolvePath(".", "mods"); + var mods = Directory.GetDirectories(basePath) + .Select(x => x.Substring(basePath.Length + 1)); + + var ret = new Dictionary(); + foreach (var mod in mods) + { + try + { + var manifest = new Manifest(mod); + ret.Add(mod, manifest); + } + catch (Exception ex) + { + Log.Write("debug", "An exception occured while trying to load mod {0}:", mod); + Log.Write("debug", ex.ToString()); + } + } + + return ret; + } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs index d633897aba..b43558d329 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs @@ -27,6 +27,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly Dictionary previews = new Dictionary(); readonly Dictionary logos = new Dictionary(); readonly Cache modInstallStatus; + readonly Cache modPrerequisitesFulfilled; readonly Widget modChooserPanel; readonly ButtonWidget loadButton; readonly SheetBuilder sheetBuilder; @@ -38,6 +39,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic [ObjectCreator.UseCtor] public ModBrowserLogic(Widget widget) { + modInstallStatus = new Cache(IsModInstalled); + modPrerequisitesFulfilled = new Cache(Game.IsModInstalled); + modChooserPanel = widget; loadButton = modChooserPanel.Get("LOAD_BUTTON"); loadButton.OnClick = () => LoadMod(selectedMod); @@ -93,8 +97,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic catch (Exception) { } } - modInstallStatus = new Cache(IsModInstalled); - ModMetadata initialMod; ModMetadata.AllMods.TryGetValue(Game.Settings.Game.PreviousMod, out initialMod); SelectMod(initialMod != null && initialMod.Id != "modchooser" ? initialMod : ModMetadata.AllMods["ra"]); @@ -157,6 +159,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic modOffset = selectedIndex - 4; loadButton.Text = modInstallStatus[mod] ? "Load Mod" : "Install Assets"; + + loadButton.Text = modPrerequisitesFulfilled[mod.Id] ? loadButton.Text : "Prerequisites missing!"; } void LoadMod(ModMetadata mod) diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 57c2ac67b3..f127fdfe10 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -4,6 +4,9 @@ Metadata: Version: {DEV_VERSION} Author: the OpenRA Developers +RequiresMods: + modchooser: {DEV_VERSION} + Folders: . ./mods/cnc diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 239899aa2e..13b7743f66 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -4,6 +4,9 @@ Metadata: Version: {DEV_VERSION} Author: the OpenRA Developers +RequiresMods: + modchooser: {DEV_VERSION} + Folders: . ./mods/d2k diff --git a/mods/modchooser/mod.yaml b/mods/modchooser/mod.yaml index 4b6d50e8bd..1af5f13d77 100644 --- a/mods/modchooser/mod.yaml +++ b/mods/modchooser/mod.yaml @@ -4,6 +4,8 @@ Metadata: Author: The OpenRA Developers Hidden: true +RequiresMods: + Folders: . ./mods/modchooser diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 31b4148707..43bd07c24e 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -4,6 +4,9 @@ Metadata: Version: {DEV_VERSION} Author: the OpenRA Developers +RequiresMods: + modchooser: {DEV_VERSION} + Folders: . ./mods/ra diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index fe85a82a54..2f48629b1e 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -4,6 +4,9 @@ Metadata: Version: {DEV_VERSION} Author: the OpenRA Developers +RequiresMods: + modchooser: {DEV_VERSION} + Folders: . # Tiberian Sun