diff --git a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs index 74ebf7c1f1..11f08da1eb 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs @@ -21,7 +21,10 @@ namespace OpenRA.Mods.Common.UpdateRules public static class UpdateUtils { - static YamlFileSet LoadYaml(ModData modData, IEnumerable files) + /// + /// Loads a YamlFileSet from a list of mod files. + /// + static YamlFileSet LoadModYaml(ModData modData, IEnumerable files) { var yaml = new YamlFileSet(); foreach (var filename in files) @@ -40,7 +43,22 @@ namespace OpenRA.Mods.Common.UpdateRules return yaml; } - static YamlFileSet LoadMapYaml(ModData modData, IReadWritePackage mapPackage, MiniYaml yaml) + /// + /// Loads a YamlFileSet containing any external yaml definitions referenced by a map yaml block. + /// + static YamlFileSet LoadExternalMapYaml(ModData modData, MiniYaml yaml) + { + return FieldLoader.GetValue("value", yaml.Value) + .Where(f => f.Contains("|")) + .SelectMany(f => LoadModYaml(modData, new[] { f })) + .ToList(); + } + + /// + /// Loads a YamlFileSet containing any internal definitions yaml referenced by a map yaml block. + /// External references or internal references to missing files are ignored. + /// + static YamlFileSet LoadInternalMapYaml(ModData modData, IReadWritePackage mapPackage, MiniYaml yaml, HashSet externalFilenames) { var fileSet = new YamlFileSet() { @@ -50,60 +68,91 @@ namespace OpenRA.Mods.Common.UpdateRules var files = FieldLoader.GetValue("value", yaml.Value); foreach (var filename in files) { + // Ignore any files that aren't in the map bundle if (!filename.Contains("|") && mapPackage.Contains(filename)) fileSet.Add(Tuple.Create(mapPackage, filename, MiniYaml.FromStream(mapPackage.GetStream(filename), filename))); - else - fileSet.AddRange(LoadYaml(modData, new[] { filename })); + else if (modData.ModFiles.Exists(filename)) + externalFilenames.Add(filename); } return fileSet; } - public static List UpdateMap(ModData modData, IReadWritePackage mapPackage, UpdateRule rule, out YamlFileSet files) + /// + /// Run a given update rule on a map. + /// The rule is only applied to internal files - external includes are assumed to be handled separately + /// but are noted in the externalFilenames list for informational purposes. + /// + public static List UpdateMap(ModData modData, IReadWritePackage mapPackage, UpdateRule rule, out YamlFileSet files, HashSet externalFilenames) { var manualSteps = new List(); - var mapStream = mapPackage.GetStream("map.yaml"); - if (mapStream == null) + using (var mapStream = mapPackage.GetStream("map.yaml")) { - // Not a valid map - files = new YamlFileSet(); - return manualSteps; + if (mapStream == null) + { + // Not a valid map + files = new YamlFileSet(); + return manualSteps; + } + + var yaml = new MiniYaml(null, MiniYaml.FromStream(mapStream, mapPackage.Name)); + files = new YamlFileSet() { Tuple.Create(mapPackage, "map.yaml", yaml.Nodes) }; + + manualSteps.AddRange(rule.BeforeUpdate(modData)); + + var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); + if (mapRulesNode != null) + { + var mapRules = LoadInternalMapYaml(modData, mapPackage, mapRulesNode.Value, externalFilenames); + manualSteps.AddRange(ApplyTopLevelTransform(modData, mapRules, rule.UpdateActorNode)); + files.AddRange(mapRules); + } + + var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); + if (mapWeaponsNode != null) + { + var mapWeapons = LoadInternalMapYaml(modData, mapPackage, mapWeaponsNode.Value, externalFilenames); + manualSteps.AddRange(ApplyTopLevelTransform(modData, mapWeapons, rule.UpdateWeaponNode)); + files.AddRange(mapWeapons); + } + + manualSteps.AddRange(rule.AfterUpdate(modData)); } - var yaml = new MiniYaml(null, MiniYaml.FromStream(mapStream, mapPackage.Name)); - files = new YamlFileSet() { Tuple.Create(mapPackage, "map.yaml", yaml.Nodes) }; - - manualSteps.AddRange(rule.BeforeUpdate(modData)); - - var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); - if (mapRulesNode != null) - { - var mapRules = LoadMapYaml(modData, mapPackage, mapRulesNode.Value); - manualSteps.AddRange(ApplyTopLevelTransform(modData, mapRules, rule.UpdateActorNode)); - files.AddRange(mapRules); - } - - var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); - if (mapWeaponsNode != null) - { - var mapWeapons = LoadMapYaml(modData, mapPackage, mapWeaponsNode.Value); - manualSteps.AddRange(ApplyTopLevelTransform(modData, mapWeapons, rule.UpdateWeaponNode)); - files.AddRange(mapWeapons); - } - - manualSteps.AddRange(rule.AfterUpdate(modData)); - return manualSteps; } public static List UpdateMod(ModData modData, UpdateRule rule, out YamlFileSet files) { var manualSteps = new List(); - var modRules = LoadYaml(modData, modData.Manifest.Rules); - var modWeapons = LoadYaml(modData, modData.Manifest.Weapons); - var modTilesets = LoadYaml(modData, modData.Manifest.TileSets); - var modChromeLayout = LoadYaml(modData, modData.Manifest.ChromeLayout); + var modRules = LoadModYaml(modData, modData.Manifest.Rules); + var modWeapons = LoadModYaml(modData, modData.Manifest.Weapons); + var modTilesets = LoadModYaml(modData, modData.Manifest.TileSets); + var modChromeLayout = LoadModYaml(modData, modData.Manifest.ChromeLayout); + + // Find and add shared map includes + foreach (var package in modData.MapCache.EnumerateMapPackagesWithoutCaching()) + { + using (var mapStream = package.GetStream("map.yaml")) + { + if (mapStream == null) + continue; + + var yaml = new MiniYaml(null, MiniYaml.FromStream(mapStream, package.Name)); + var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); + if (mapRulesNode != null) + foreach (var f in LoadExternalMapYaml(modData, mapRulesNode.Value)) + if (!modRules.Any(m => m.Item1 == f.Item1 && m.Item2 == f.Item2)) + modRules.Add(f); + + var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); + if (mapWeaponsNode != null) + foreach (var f in LoadExternalMapYaml(modData, mapWeaponsNode.Value)) + if (!modWeapons.Any(m => m.Item1 == f.Item1 && m.Item2 == f.Item2)) + modWeapons.Add(f); + } + } manualSteps.AddRange(rule.BeforeUpdate(modData)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modRules, rule.UpdateActorNode)); diff --git a/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs index 488159d150..54f8cbf9c1 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs @@ -82,9 +82,10 @@ namespace OpenRA.Mods.Common.UtilityCommands Console.Write(" Updating map... "); + var externalFilenames = new HashSet(); try { - manualSteps = UpdateUtils.UpdateMap(modData, mapPackage, rule, out mapFiles); + manualSteps = UpdateUtils.UpdateMap(modData, mapPackage, rule, out mapFiles, externalFilenames); } catch (Exception ex) { @@ -104,6 +105,13 @@ namespace OpenRA.Mods.Common.UtilityCommands mapFiles.Save(); Console.WriteLine("COMPLETE"); + if (externalFilenames.Any()) + { + Console.WriteLine(" The following shared yaml files referenced by the map have been ignored:"); + Console.WriteLine(UpdateUtils.FormatMessageList(externalFilenames, 1)); + Console.WriteLine(" These files are assumed to have already been updated by the --update-mod command"); + } + if (manualSteps.Any()) { Console.WriteLine(" Manual changes are required to complete this update:"); diff --git a/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs index 4b64c28ab3..7288579185 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs @@ -146,15 +146,17 @@ namespace OpenRA.Mods.Common.UtilityCommands } Console.Write(" Updating system maps... "); + if (!skipMaps) { var mapsFailed = false; + var externalFilenames = new HashSet(); foreach (var package in modData.MapCache.EnumerateMapPackagesWithoutCaching()) { try { YamlFileSet mapFiles; - var mapSteps = UpdateUtils.UpdateMap(modData, package, rule, out mapFiles); + var mapSteps = UpdateUtils.UpdateMap(modData, package, rule, out mapFiles, externalFilenames); allFiles.AddRange(mapFiles); if (mapSteps.Any())