diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index b70207ba27..22e9f344c1 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -123,10 +123,12 @@ namespace OpenRA mapDirectoryTrackers.Add(new MapDirectoryTracker(mapGrid, package, classification)); } + // PERF: Load the mod YAML once outside the loop, and reuse it when resolving each maps custom YAML. + var modDataRules = modData.GetRulesYaml(); foreach (var kv in MapLocations) { foreach (var map in kv.Key.Contents) - LoadMap(map, kv.Key, kv.Value, mapGrid, null); + LoadMapInternal(map, kv.Key, kv.Value, mapGrid, null, modDataRules); } // We only want to track maps in runtime, not at loadtime @@ -134,6 +136,11 @@ namespace OpenRA } public void LoadMap(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap) + { + LoadMapInternal(map, package, classification, mapGrid, oldMap, null); + } + + void LoadMapInternal(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap, IEnumerable> modDataRules) { IReadOnlyPackage mapPackage = null; try @@ -144,7 +151,7 @@ namespace OpenRA if (mapPackage != null) { var uid = Map.ComputeUID(mapPackage); - previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type); + previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type, modDataRules); if (oldMap != uid) { diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index 3c9b0609d1..23a367ff67 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -111,7 +111,7 @@ namespace OpenRA return key == "world" || key == "player"; } - public void SetCustomRules(ModData modData, IReadOnlyFileSystem fileSystem, Dictionary yaml) + public void SetCustomRules(ModData modData, IReadOnlyFileSystem fileSystem, Dictionary yaml, IEnumerable> modDataRules) { RuleDefinitions = LoadRuleSection(yaml, "Rules"); WeaponDefinitions = LoadRuleSection(yaml, "Weapons"); @@ -131,14 +131,17 @@ namespace OpenRA // This assumes/enforces that these actor types can only inherit abstract definitions (starting with ^) if (RuleDefinitions != null) { - var files = modData.Manifest.Rules.AsEnumerable(); + modDataRules ??= modData.GetRulesYaml(); + var files = Enumerable.Empty(); if (RuleDefinitions.Value != null) { var mapFiles = FieldLoader.GetValue("value", RuleDefinitions.Value); files = files.Append(mapFiles); } - var sources = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList()); + var sources = + modDataRules.Select(x => x.Where(IsLoadableRuleDefinition).ToList()) + .Concat(files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList())); if (RuleDefinitions.Nodes.Count > 0) sources = sources.Append(RuleDefinitions.Nodes.Where(IsLoadableRuleDefinition).ToList()); @@ -319,10 +322,10 @@ namespace OpenRA { "Notifications", map.NotificationDefinitions }, { "Sequences", map.SequenceDefinitions }, { "ModelSequences", map.ModelSequenceDefinitions } - }); + }, null); } - public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType) + public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType, IEnumerable> modDataRules) { Dictionary yaml; using (var yamlStream = p.GetStream("map.yaml")) @@ -412,7 +415,7 @@ namespace OpenRA newData.Status = MapStatus.Unavailable; } - newData.SetCustomRules(modData, this, yaml); + newData.SetCustomRules(modData, this, yaml, modDataRules); if (cache.LoadPreviewImages && p.Contains("map.png")) using (var dataStream = p.GetStream("map.png")) @@ -475,7 +478,7 @@ namespace OpenRA var rulesString = Encoding.UTF8.GetString(Convert.FromBase64String(r.rules)); var rulesYaml = new MiniYaml("", MiniYaml.FromString(rulesString)).ToDictionary(); - newData.SetCustomRules(modData, this, rulesYaml); + newData.SetCustomRules(modData, this, rulesYaml, null); } catch (Exception e) { @@ -554,7 +557,7 @@ namespace OpenRA innerData.Status = MapStatus.DownloadError; else { - UpdateFromMap(package, mapInstallPackage, MapClassification.User, null, GridType); + UpdateFromMap(package, mapInstallPackage, MapClassification.User, null, GridType, null); Game.RunAfterTick(onSuccess); } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index e6d485330f..f22a4daf43 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -166,6 +166,11 @@ namespace OpenRA return map; } + public List[] GetRulesYaml() + { + return Manifest.Rules.Select(s => MiniYaml.FromStream(DefaultFileSystem.Open(s), s)).ToArray(); + } + public void Dispose() { LoadScreen?.Dispose();