diff --git a/OpenRA.Game/ExternalMods.cs b/OpenRA.Game/ExternalMods.cs index 3decd6b20c..40dc1c30c4 100644 --- a/OpenRA.Game/ExternalMods.cs +++ b/OpenRA.Game/ExternalMods.cs @@ -94,17 +94,17 @@ namespace OpenRA if (sheetBuilder != null) { - var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon"); + var iconNode = yaml.NodeWithKeyOrDefault("Icon"); if (iconNode != null && !string.IsNullOrEmpty(iconNode.Value.Value)) using (var stream = new MemoryStream(Convert.FromBase64String(iconNode.Value.Value))) mod.Icon = sheetBuilder.Add(new Png(stream)); - var icon2xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon2x"); + var icon2xNode = yaml.NodeWithKeyOrDefault("Icon2x"); if (icon2xNode != null && !string.IsNullOrEmpty(icon2xNode.Value.Value)) using (var stream = new MemoryStream(Convert.FromBase64String(icon2xNode.Value.Value))) mod.Icon2x = sheetBuilder.Add(new Png(stream), 1f / 2); - var icon3xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon3x"); + var icon3xNode = yaml.NodeWithKeyOrDefault("Icon3x"); if (icon3xNode != null && !string.IsNullOrEmpty(icon3xNode.Value.Value)) using (var stream = new MemoryStream(Convert.FromBase64String(icon3xNode.Value.Value))) mod.Icon3x = sheetBuilder.Add(new Png(stream), 1f / 3); diff --git a/OpenRA.Game/GameRules/SoundInfo.cs b/OpenRA.Game/GameRules/SoundInfo.cs index b42fd281f2..e2711ccf45 100644 --- a/OpenRA.Game/GameRules/SoundInfo.cs +++ b/OpenRA.Game/GameRules/SoundInfo.cs @@ -40,16 +40,16 @@ namespace OpenRA.GameRules static Dictionary ParseSoundPool(MiniYaml y, string key) { var ret = new Dictionary(); - var classifiction = y.Nodes.First(x => x.Key == key); + var classifiction = y.NodeWithKey(key); foreach (var t in classifiction.Value.Nodes) { var volumeModifier = 1f; - var volumeModifierNode = t.Value.Nodes.FirstOrDefault(x => x.Key == nameof(SoundPool.VolumeModifier)); + var volumeModifierNode = t.Value.NodeWithKeyOrDefault(nameof(SoundPool.VolumeModifier)); if (volumeModifierNode != null) volumeModifier = FieldLoader.GetValue(volumeModifierNode.Key, volumeModifierNode.Value.Value); var interruptType = SoundPool.DefaultInterruptType; - var interruptTypeNode = t.Value.Nodes.FirstOrDefault(x => x.Key == nameof(SoundPool.InterruptType)); + var interruptTypeNode = t.Value.NodeWithKeyOrDefault(nameof(SoundPool.InterruptType)); if (interruptTypeNode != null) interruptType = FieldLoader.GetValue(interruptTypeNode.Key, interruptTypeNode.Value.Value); diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index b4b76807dc..bac06ff470 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -145,7 +145,8 @@ namespace OpenRA.GameRules static object LoadProjectile(MiniYaml yaml) { - if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj)) + var proj = yaml.NodeWithKeyOrDefault("Projectile")?.Value; + if (proj == null) return null; var ret = Game.CreateObject(proj.Value + "Info"); diff --git a/OpenRA.Game/GameSpeed.cs b/OpenRA.Game/GameSpeed.cs index 17233e4a52..ccb93d6c5a 100644 --- a/OpenRA.Game/GameSpeed.cs +++ b/OpenRA.Game/GameSpeed.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; namespace OpenRA { @@ -38,7 +37,7 @@ namespace OpenRA static object LoadSpeeds(MiniYaml y) { var ret = new Dictionary(); - var speedsNode = y.Nodes.FirstOrDefault(n => n.Key == "Speeds"); + var speedsNode = y.NodeWithKeyOrDefault("Speeds"); if (speedsNode == null) throw new YamlException("Error parsing GameSpeeds: Missing Speeds node!"); diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 04c623885e..85919dbe7d 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -27,7 +27,7 @@ namespace OpenRA.Graphics var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select( s => MiniYaml.FromStream(fileSystem.Open(s), s))); - var nodesDict = new MiniYaml(null, sequenceYaml).ToDictionary(); + var cursorsYaml = new MiniYaml(null, sequenceYaml).NodeWithKey("Cursors").Value; // Overwrite previous definitions if there are duplicates var pals = new Dictionary(); @@ -35,14 +35,14 @@ namespace OpenRA.Graphics if (p.Palette != null) pals[p.Palette] = p; - Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value) + Palettes = cursorsYaml.Nodes.Select(n => n.Value.Value) .Where(p => p != null) .Distinct() .ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem)); var frameCache = new FrameCache(fileSystem, modData.SpriteLoaders); var cursors = new Dictionary(); - foreach (var s in nodesDict["Cursors"].Nodes) + foreach (var s in cursorsYaml.Nodes) foreach (var sequence in s.Value.Nodes) cursors.Add(sequence.Key, new CursorSequence(frameCache, sequence.Key, s.Key, s.Value.Value, sequence.Value)); diff --git a/OpenRA.Game/HotkeyDefinition.cs b/OpenRA.Game/HotkeyDefinition.cs index 84eef05295..ad4a09a85a 100644 --- a/OpenRA.Game/HotkeyDefinition.cs +++ b/OpenRA.Game/HotkeyDefinition.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; namespace OpenRA { @@ -31,29 +30,26 @@ namespace OpenRA if (!string.IsNullOrEmpty(node.Value)) Default = FieldLoader.GetValue("value", node.Value); - var descriptionNode = node.Nodes.FirstOrDefault(n => n.Key == "Description"); - if (descriptionNode != null) - Description = descriptionNode.Value.Value; + var nodeDict = node.ToDictionary(); - var typesNode = node.Nodes.FirstOrDefault(n => n.Key == "Types"); - if (typesNode != null) - Types = FieldLoader.GetValue>("Types", typesNode.Value.Value); + if (nodeDict.TryGetValue("Description", out var descriptionYaml)) + Description = descriptionYaml.Value; - var contextsNode = node.Nodes.FirstOrDefault(n => n.Key == "Contexts"); - if (contextsNode != null) - Contexts = FieldLoader.GetValue>("Contexts", contextsNode.Value.Value); + if (nodeDict.TryGetValue("Types", out var typesYaml)) + Types = FieldLoader.GetValue>("Types", typesYaml.Value); - var platformNode = node.Nodes.FirstOrDefault(n => n.Key == "Platform"); - if (platformNode != null) + if (nodeDict.TryGetValue("Contexts", out var contextYaml)) + Contexts = FieldLoader.GetValue>("Contexts", contextYaml.Value); + + if (nodeDict.TryGetValue("Platform", out var platformYaml)) { - var platformOverride = platformNode.Value.Nodes.FirstOrDefault(n => n.Key == Platform.CurrentPlatform.ToString()); + var platformOverride = platformYaml.NodeWithKeyOrDefault(Platform.CurrentPlatform.ToString()); if (platformOverride != null) Default = FieldLoader.GetValue("value", platformOverride.Value.Value); } - var readonlyNode = node.Nodes.FirstOrDefault(n => n.Key == "Readonly"); - if (readonlyNode != null) - Readonly = FieldLoader.GetValue("Readonly", readonlyNode.Value.Value); + if (nodeDict.TryGetValue("Readonly", out var readonlyYaml)) + Readonly = FieldLoader.GetValue("Readonly", readonlyYaml.Value); } } } diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 4405cfc684..8f3da202da 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -214,7 +214,7 @@ namespace OpenRA if (!yaml.ContainsKey(key)) return Array.Empty(); - return yaml[key].ToDictionary().Keys.ToArray(); + return yaml[key].Nodes.Select(n => n.Key).ToArray(); } static IReadOnlyDictionary YamlDictionary(Dictionary yaml, string key) diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 2b386c1576..f506b2e733 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -95,9 +95,9 @@ namespace OpenRA t == typeof(MiniYaml) ? Type.MiniYaml : Type.Normal; } - public void Deserialize(Map map, ImmutableArray nodes) + public void Deserialize(Map map, MiniYaml yaml) { - var node = nodes.FirstOrDefault(n => n.Key == key); + var node = yaml.NodeWithKeyOrDefault(key); if (node == null) { if (required) @@ -363,13 +363,13 @@ namespace OpenRA var yaml = new MiniYaml(null, MiniYaml.FromStream(Package.GetStream("map.yaml"), package.Name)); foreach (var field in YamlFields) - field.Deserialize(this, yaml.Nodes); + field.Deserialize(this, yaml); if (MapFormat < SupportedMapFormat) throw new InvalidDataException($"Map format {MapFormat} is not supported.\n File: {package.Name}"); - PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players"); - ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors"); + PlayerDefinitions = yaml.NodeWithKeyOrDefault("Players")?.Value.Nodes ?? ImmutableArray.Empty; + ActorDefinitions = yaml.NodeWithKeyOrDefault("Actors")?.Value.Nodes ?? ImmutableArray.Empty; Grid = modData.Manifest.Get(); diff --git a/OpenRA.Game/MiniYaml.cs b/OpenRA.Game/MiniYaml.cs index a28ca60fd0..7807389a7b 100644 --- a/OpenRA.Game/MiniYaml.cs +++ b/OpenRA.Game/MiniYaml.cs @@ -141,6 +141,34 @@ namespace OpenRA return new MiniYaml(Value, newNodes); } + public MiniYamlNode NodeWithKey(string key) + { + var result = NodeWithKeyOrDefault(key); + if (result == null) + throw new InvalidDataException($"No node with key '{key}'"); + return result; + } + + public MiniYamlNode NodeWithKeyOrDefault(string key) + { + // PERF: Avoid LINQ. + var first = true; + MiniYamlNode result = null; + foreach (var node in Nodes) + { + if (node.Key != key) + continue; + + if (!first) + throw new InvalidDataException($"Duplicate key '{node.Key}' in {node.Location}"); + + first = false; + result = node; + } + + return result; + } + public Dictionary ToDictionary() { return ToDictionary(MiniYamlIdentity); @@ -175,11 +203,6 @@ namespace OpenRA Nodes = ImmutableArray.CreateRange(nodes); } - public static ImmutableArray NodesOrEmpty(MiniYaml y, string s) - { - return y.Nodes.FirstOrDefault(n => n.Key == s)?.Value.Nodes ?? ImmutableArray.Empty; - } - static List FromLines(IEnumerable> lines, string filename, bool discardCommentsAndWhitespace, Dictionary stringPool) { stringPool ??= new Dictionary(); @@ -668,6 +691,11 @@ namespace OpenRA foreach (var line in Nodes.ToLines()) yield return "\t" + line; } + + public MiniYamlNodeBuilder NodeWithKeyOrDefault(string key) + { + return Nodes.SingleOrDefault(n => n.Key == key); + } } [Serializable] diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs index 63d9c11aa3..7a7b766b18 100644 --- a/OpenRA.Game/Network/GameServer.cs +++ b/OpenRA.Game/Network/GameServer.cs @@ -140,7 +140,7 @@ namespace OpenRA.Network static object LoadClients(MiniYaml yaml) { var clients = new List(); - var clientsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Clients"); + var clientsNode = yaml.NodeWithKeyOrDefault("Clients"); if (clientsNode != null) { var regex = new Regex(@"Client@\d+"); @@ -159,7 +159,7 @@ namespace OpenRA.Network // Games advertised using the old API used a single Mods field if (Mod == null || Version == null) { - var modsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Mods"); + var modsNode = yaml.NodeWithKeyOrDefault("Mods"); if (modsNode != null) { var modVersion = modsNode.Value.Value.Split('@'); diff --git a/OpenRA.Game/Network/LocalizedMessage.cs b/OpenRA.Game/Network/LocalizedMessage.cs index 4deb7b7330..3a0feaa9ca 100644 --- a/OpenRA.Game/Network/LocalizedMessage.cs +++ b/OpenRA.Game/Network/LocalizedMessage.cs @@ -60,7 +60,7 @@ namespace OpenRA.Network static object LoadArguments(MiniYaml yaml) { var arguments = new Dictionary(); - var argumentsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Arguments"); + var argumentsNode = yaml.NodeWithKeyOrDefault("Arguments"); if (argumentsNode != null) { foreach (var argumentNode in argumentsNode.Value.Nodes) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 1c7b21b293..c916e700c8 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -227,7 +227,7 @@ namespace OpenRA.Network { var gs = FieldLoader.Load(data); - var optionsNode = data.Nodes.FirstOrDefault(n => n.Key == "Options"); + var optionsNode = data.NodeWithKeyOrDefault("Options"); if (optionsNode != null) foreach (var n in optionsNode.Value.Nodes) gs.LobbyOptions[n.Key] = FieldLoader.Load(n.Value); diff --git a/OpenRA.Game/PlayerDatabase.cs b/OpenRA.Game/PlayerDatabase.cs index 3a98f5aaa9..c02895d09a 100644 --- a/OpenRA.Game/PlayerDatabase.cs +++ b/OpenRA.Game/PlayerDatabase.cs @@ -9,7 +9,6 @@ */ #endregion -using System.Linq; using System.Threading.Tasks; using OpenRA.FileFormats; using OpenRA.Graphics; @@ -94,10 +93,10 @@ namespace OpenRA }); } - var labelNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Label"); - var icon24Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon24"); - var icon48Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon48"); - var icon72Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon72"); + var labelNode = yaml.NodeWithKeyOrDefault("Label"); + var icon24Node = yaml.NodeWithKeyOrDefault("Icon24"); + var icon48Node = yaml.NodeWithKeyOrDefault("Icon48"); + var icon72Node = yaml.NodeWithKeyOrDefault("Icon72"); if (labelNode == null) return null; diff --git a/OpenRA.Game/PlayerProfile.cs b/OpenRA.Game/PlayerProfile.cs index d8c72c53bc..b9a74a8efd 100644 --- a/OpenRA.Game/PlayerProfile.cs +++ b/OpenRA.Game/PlayerProfile.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; namespace OpenRA { @@ -31,7 +30,7 @@ namespace OpenRA { var badges = new List(); - var badgesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Badges"); + var badgesNode = yaml.NodeWithKeyOrDefault("Badges"); if (badgesNode != null) { var playerDatabase = Game.ModData.Manifest.Get(); diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index e7e5847db5..11c7de0a3f 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -385,7 +385,7 @@ namespace OpenRA else { // Update or add the custom value - var fieldYaml = sectionYaml.Value.Nodes.FirstOrDefault(n => n.Key == fli.YamlName); + var fieldYaml = sectionYaml.Value.NodeWithKeyOrDefault(fli.YamlName); if (fieldYaml != null) fieldYaml.Value.Value = serialized; else diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 57154dc125..b0923d3c63 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -350,7 +349,7 @@ namespace OpenRA.Traits public interface IGameSaveTraitData { List IssueTraitData(Actor self); - void ResolveTraitData(Actor self, ImmutableArray data); + void ResolveTraitData(Actor self, MiniYaml data); } [RequireExplicitImplementation] diff --git a/OpenRA.Game/Widgets/WidgetLoader.cs b/OpenRA.Game/Widgets/WidgetLoader.cs index af402b5363..8cdbdb0777 100644 --- a/OpenRA.Game/Widgets/WidgetLoader.cs +++ b/OpenRA.Game/Widgets/WidgetLoader.cs @@ -66,7 +66,7 @@ namespace OpenRA foreach (var c in child.Value.Nodes) LoadWidget(args, widget, c); - var logicNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Logic"); + var logicNode = node.Value.NodeWithKeyOrDefault("Logic"); var logic = logicNode?.Value.ToDictionary(); args.Add("logicArgs", logic); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index d4b3d27793..6a69c15aee 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -423,7 +423,7 @@ namespace OpenRA if (tp.Actor == null) break; - tp.Trait.ResolveTraitData(tp.Actor, kv.Value.Nodes); + tp.Trait.ResolveTraitData(tp.Actor, kv.Value); } gameSaveTraitData.Clear(); diff --git a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs index b0ed9dcc0d..783290cfe1 100644 --- a/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Cnc/Graphics/ClassicTilesetSpecificSpriteSequence.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; @@ -39,10 +38,10 @@ namespace OpenRA.Mods.Cnc.Graphics protected override IEnumerable ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) { - var node = data.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key); + var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) { - var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + var tilesetNode = node.Value.NodeWithKeyOrDefault(tileset); if (tilesetNode != null) { var loadFrames = CalculateFrameIndices(start, length, stride ?? length ?? 0, facings, frames, transpose, reverseFacings, shadowStart); @@ -55,10 +54,10 @@ namespace OpenRA.Mods.Cnc.Graphics protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) { - var node = data.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key); + var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) { - var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + var tilesetNode = node.Value.NodeWithKeyOrDefault(tileset); if (tilesetNode != null) { if (frames == null) diff --git a/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs b/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs index acbff3e9d0..1f2ae3deca 100644 --- a/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs +++ b/OpenRA.Mods.Cnc/UtilityCommands/ImportGen1MapCommand.cs @@ -130,7 +130,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands protected MiniYamlNodeBuilder GetWorldNodeBuilderFromRules() { - var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World"); + var worldNode = Map.RuleDefinitions.NodeWithKeyOrDefault("World"); var worldNodeBuilder = worldNode != null ? new MiniYamlNodeBuilder(worldNode) : new MiniYamlNodeBuilder("World", new MiniYamlBuilder("", new List())); @@ -166,7 +166,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands var worldNodeBuilder = GetWorldNodeBuilderFromRules(); - var missionData = worldNodeBuilder.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData"); + var missionData = worldNodeBuilder.Value.NodeWithKeyOrDefault("MissionData"); if (missionData == null) { missionData = new MiniYamlNodeBuilder("MissionData", new MiniYamlBuilder("", new List())); @@ -237,7 +237,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands { var worldNodeBuilder = GetWorldNodeBuilderFromRules(); - var missionData = worldNodeBuilder.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData"); + var missionData = worldNodeBuilder.Value.NodeWithKeyOrDefault("MissionData"); if (missionData == null) { missionData = new MiniYamlNodeBuilder("MissionData", new MiniYamlBuilder("", new List())); diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index 0f70ece2ff..5fbc5cb0c8 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Graphics IReadOnlyDictionary ISpriteSequenceLoader.ParseSequences(ModData modData, string tileset, SpriteCache cache, MiniYamlNode imageNode) { var sequences = new Dictionary(); - var node = imageNode.Value.Nodes.SingleOrDefault(n => n.Key == "Defaults"); + var node = imageNode.Value.NodeWithKeyOrDefault("Defaults"); var defaults = node?.Value ?? NoData; imageNode = imageNode.WithValue(imageNode.Value.WithNodes(imageNode.Value.Nodes.Remove(node))); @@ -262,7 +262,7 @@ namespace OpenRA.Mods.Common.Graphics protected static T LoadField(string key, T fallback, MiniYaml data, MiniYaml defaults = null) { - var node = data.Nodes.FirstOrDefault(n => n.Key == key) ?? defaults?.Nodes.FirstOrDefault(n => n.Key == key); + var node = data.NodeWithKeyOrDefault(key) ?? defaults?.NodeWithKeyOrDefault(key); if (node == null) return fallback; @@ -276,7 +276,7 @@ namespace OpenRA.Mods.Common.Graphics protected static T LoadField(SpriteSequenceField field, MiniYaml data, MiniYaml defaults, out MiniYamlNode.SourceLocation location) { - var node = data.Nodes.FirstOrDefault(n => n.Key == field.Key) ?? defaults?.Nodes.FirstOrDefault(n => n.Key == field.Key); + var node = data.NodeWithKeyOrDefault(field.Key) ?? defaults?.NodeWithKeyOrDefault(field.Key); if (node == null) { location = default; @@ -414,7 +414,7 @@ namespace OpenRA.Mods.Common.Graphics var offset = LoadField(Offset, data, defaults); var blendMode = LoadField(BlendMode, data, defaults); - var combineNode = data.Nodes.FirstOrDefault(n => n.Key == Combine.Key); + var combineNode = data.NodeWithKeyOrDefault(Combine.Key); if (combineNode != null) { for (var i = 0; i < combineNode.Value.Nodes.Length; i++) diff --git a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs index d1f3b49b35..40046118d6 100644 --- a/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/TilesetSpecificSpriteSequence.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; using OpenRA.Graphics; namespace OpenRA.Mods.Common.Graphics @@ -37,10 +36,10 @@ namespace OpenRA.Mods.Common.Graphics protected override IEnumerable ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) { - var node = data.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key); + var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) { - var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + var tilesetNode = node.Value.NodeWithKeyOrDefault(tileset); if (tilesetNode != null) { var loadFrames = CalculateFrameIndices(start, length, stride ?? length ?? 0, facings, frames, transpose, reverseFacings, shadowStart); @@ -53,10 +52,10 @@ namespace OpenRA.Mods.Common.Graphics protected override IEnumerable ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) { - var node = data.Nodes.FirstOrDefault(n => n.Key == TilesetFilenames.Key); + var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); if (node != null) { - var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + var tilesetNode = node.Value.NodeWithKeyOrDefault(tileset); if (tilesetNode != null) { if (frames == null) diff --git a/OpenRA.Mods.Common/Installer/InstallerUtils.cs b/OpenRA.Mods.Common/Installer/InstallerUtils.cs index 726da2ac01..fd711ba9e4 100644 --- a/OpenRA.Mods.Common/Installer/InstallerUtils.cs +++ b/OpenRA.Mods.Common/Installer/InstallerUtils.cs @@ -11,7 +11,6 @@ using System; using System.IO; -using System.Linq; using FS = OpenRA.FileSystem.FileSystem; namespace OpenRA.Mods.Common.Installer @@ -33,8 +32,8 @@ namespace OpenRA.Mods.Common.Installer using (var fileStream = File.OpenRead(filePath)) { - var offsetNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Offset"); - var lengthNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Length"); + var offsetNode = kv.Value.NodeWithKeyOrDefault("Offset"); + var lengthNode = kv.Value.NodeWithKeyOrDefault("Length"); if (offsetNode != null || lengthNode != null) { var offset = 0L; diff --git a/OpenRA.Mods.Common/Installer/SourceActions/ExtractIscabSourceAction.cs b/OpenRA.Mods.Common/Installer/SourceActions/ExtractIscabSourceAction.cs index 08dd1acc5d..1a497bfae3 100644 --- a/OpenRA.Mods.Common/Installer/SourceActions/ExtractIscabSourceAction.cs +++ b/OpenRA.Mods.Common/Installer/SourceActions/ExtractIscabSourceAction.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using OpenRA.Mods.Common.FileFormats; using OpenRA.Mods.Common.Widgets.Logic; using FS = OpenRA.FileSystem.FileSystem; @@ -26,11 +25,11 @@ namespace OpenRA.Mods.Common.Installer // Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected source path var sourcePath = actionYaml.Value.StartsWith('^') ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(Path.Combine(path, actionYaml.Value)); - var volumeNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Volumes"); + var volumeNode = actionYaml.NodeWithKeyOrDefault("Volumes"); if (volumeNode == null) throw new InvalidDataException("extract-iscab entry doesn't define a Volumes node"); - var extractNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Extract"); + var extractNode = actionYaml.NodeWithKeyOrDefault("Extract"); if (extractNode == null) throw new InvalidDataException("extract-iscab entry doesn't define an Extract node"); diff --git a/OpenRA.Mods.Common/Installer/SourceActions/ExtractRawSourceAction.cs b/OpenRA.Mods.Common/Installer/SourceActions/ExtractRawSourceAction.cs index 70c0f4e9ba..4a237c28d6 100644 --- a/OpenRA.Mods.Common/Installer/SourceActions/ExtractRawSourceAction.cs +++ b/OpenRA.Mods.Common/Installer/SourceActions/ExtractRawSourceAction.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using OpenRA.Mods.Common.Widgets.Logic; using FS = OpenRA.FileSystem.FileSystem; @@ -37,14 +36,14 @@ namespace OpenRA.Mods.Common.Installer continue; } - var offsetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Offset"); + var offsetNode = node.Value.NodeWithKeyOrDefault("Offset"); if (offsetNode == null) { Log.Write("install", "Skipping entry with missing Offset definition " + targetPath); continue; } - var lengthNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Length"); + var lengthNode = node.Value.NodeWithKeyOrDefault("Length"); if (lengthNode == null) { Log.Write("install", "Skipping entry with missing Length definition " + targetPath); diff --git a/OpenRA.Mods.Common/Installer/SourceResolvers/GogSourceResolver.cs b/OpenRA.Mods.Common/Installer/SourceResolvers/GogSourceResolver.cs index 0a7c8323b7..29d7c2b900 100644 --- a/OpenRA.Mods.Common/Installer/SourceResolvers/GogSourceResolver.cs +++ b/OpenRA.Mods.Common/Installer/SourceResolvers/GogSourceResolver.cs @@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Installer { public string FindSourcePath(ModContent.ModSource modSource) { - modSource.Type.ToDictionary().TryGetValue("AppId", out var appId); + var appId = modSource.Type.NodeWithKeyOrDefault("AppId"); if (appId == null) return null; @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Installer foreach (var prefix in prefixes) { - if (Registry.GetValue($"{prefix}GOG.com\\Games\\{appId.Value}", "path", null) is not string installDir) + if (Registry.GetValue($"{prefix}GOG.com\\Games\\{appId.Value.Value}", "path", null) is not string installDir) continue; if (InstallerUtils.IsValidSourcePath(installDir, modSource)) diff --git a/OpenRA.Mods.Common/Installer/SourceResolvers/SteamSourceResolver.cs b/OpenRA.Mods.Common/Installer/SourceResolvers/SteamSourceResolver.cs index 712bdd1f8d..a2d1e85968 100644 --- a/OpenRA.Mods.Common/Installer/SourceResolvers/SteamSourceResolver.cs +++ b/OpenRA.Mods.Common/Installer/SourceResolvers/SteamSourceResolver.cs @@ -23,14 +23,14 @@ namespace OpenRA.Mods.Common.Installer { public string FindSourcePath(ModContent.ModSource modSource) { - modSource.Type.ToDictionary().TryGetValue("AppId", out var appId); + var appId = modSource.Type.NodeWithKeyOrDefault("AppId"); if (appId == null) return null; foreach (var steamDirectory in SteamDirectory()) { - var manifestPath = Path.Combine(steamDirectory, "steamapps", $"appmanifest_{appId.Value}.acf"); + var manifestPath = Path.Combine(steamDirectory, "steamapps", $"appmanifest_{appId.Value.Value}.acf"); if (!File.Exists(manifestPath)) continue; diff --git a/OpenRA.Mods.Common/Lint/CheckCursors.cs b/OpenRA.Mods.Common/Lint/CheckCursors.cs index 43d3ebd39a..76f58b3f1a 100644 --- a/OpenRA.Mods.Common/Lint/CheckCursors.cs +++ b/OpenRA.Mods.Common/Lint/CheckCursors.cs @@ -33,11 +33,11 @@ namespace OpenRA.Mods.Common.Lint { var fileSystem = modData.DefaultFileSystem; var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s))); - var nodesDict = new MiniYaml(null, sequenceYaml).ToDictionary(); + var cursorsYaml = new MiniYaml(null, sequenceYaml).NodeWithKey("Cursors").Value; // Avoid using CursorProvider as it attempts to load palettes from the file system. var cursors = new List(); - foreach (var s in nodesDict["Cursors"].Nodes) + foreach (var s in cursorsYaml.Nodes) foreach (var sequence in s.Value.Nodes) cursors.Add(sequence.Key); diff --git a/OpenRA.Mods.Common/ModContent.cs b/OpenRA.Mods.Common/ModContent.cs index f5358b6397..81fcab8fde 100644 --- a/OpenRA.Mods.Common/ModContent.cs +++ b/OpenRA.Mods.Common/ModContent.cs @@ -66,15 +66,15 @@ namespace OpenRA ObjectCreator = objectCreator; Title = yaml.Value; - var type = yaml.Nodes.FirstOrDefault(n => n.Key == "Type"); + var type = yaml.NodeWithKeyOrDefault("Type"); if (type != null) Type = type.Value; - var idFiles = yaml.Nodes.FirstOrDefault(n => n.Key == "IDFiles"); + var idFiles = yaml.NodeWithKeyOrDefault("IDFiles"); if (idFiles != null) IDFiles = idFiles.Value; - var installNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Install"); + var installNode = yaml.NodeWithKeyOrDefault("Install"); if (installNode != null) Install = installNode.Value.Nodes; @@ -111,7 +111,7 @@ namespace OpenRA static object LoadPackages(MiniYaml yaml) { var packages = new Dictionary(); - var packageNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Packages"); + var packageNode = yaml.NodeWithKeyOrDefault("Packages"); if (packageNode != null) foreach (var node in packageNode.Value.Nodes) packages.Add(node.Key, new ModPackage(node.Value)); @@ -124,7 +124,7 @@ namespace OpenRA static object LoadDownloads(MiniYaml yaml) { - var downloadNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Downloads"); + var downloadNode = yaml.NodeWithKeyOrDefault("Downloads"); return downloadNode != null ? downloadNode.Value.Nodes.Select(n => n.Key).ToArray() : Array.Empty(); } @@ -133,7 +133,7 @@ namespace OpenRA static object LoadSources(MiniYaml yaml) { - var sourceNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Sources"); + var sourceNode = yaml.NodeWithKeyOrDefault("Sources"); return sourceNode != null ? sourceNode.Value.Nodes.Select(n => n.Key).ToArray() : Array.Empty(); } } diff --git a/OpenRA.Mods.Common/Terrain/TerrainInfo.cs b/OpenRA.Mods.Common/Terrain/TerrainInfo.cs index 7fcb834482..f0a06344f0 100644 --- a/OpenRA.Mods.Common/Terrain/TerrainInfo.cs +++ b/OpenRA.Mods.Common/Terrain/TerrainInfo.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Terrain { FieldLoader.Load(this, my); - var nodes = my.ToDictionary()["Tiles"].Nodes; + var nodes = my.NodeWithKey("Tiles").Value.Nodes; if (!PickAny) { diff --git a/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs b/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs index 79e0149200..902fa40256 100644 --- a/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs +++ b/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits else { var owner = map.PlayerDefinitions.Single(p => s.Get().InternalName == p.Value.Nodes.Last(k => k.Key == "Name").Value.Value); - var colorValue = owner.Value.Nodes.FirstOrDefault(n => n.Key == "Color"); + var colorValue = owner.Value.NodeWithKeyOrDefault("Color"); var ownerColor = colorValue?.Value.Value ?? "FFFFFF"; Color.TryParse(ownerColor, out color); } diff --git a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs index 9edb861aca..67ba202ec0 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -326,16 +325,16 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { if (self.World.IsReplay) return; - var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); + var initialBaseCenterNode = data.NodeWithKeyOrDefault("InitialBaseCenter"); if (initialBaseCenterNode != null) initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); - var defenseCenterNode = data.FirstOrDefault(n => n.Key == "DefenseCenter"); + var defenseCenterNode = data.NodeWithKeyOrDefault("DefenseCenter"); if (defenseCenterNode != null) DefenseCenter = FieldLoader.GetValue("DefenseCenter", defenseCenterNode.Value.Value); } diff --git a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs index 3aa3c0514a..ebb5b8f0ae 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -213,12 +212,12 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { if (self.World.IsReplay) return; - var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); + var initialBaseCenterNode = data.NodeWithKeyOrDefault("InitialBaseCenter"); if (initialBaseCenterNode != null) initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); } diff --git a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs index 6793bedda0..ca5588f8b3 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Mods.Common.Traits.BotModules.Squads; using OpenRA.Primitives; @@ -526,52 +525,46 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { if (self.World.IsReplay) return; - var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); - if (initialBaseCenterNode != null) - initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); + var nodes = data.ToDictionary(); - var unitsHangingAroundTheBaseNode = data.FirstOrDefault(n => n.Key == "UnitsHangingAroundTheBase"); - if (unitsHangingAroundTheBaseNode != null) + if (nodes.TryGetValue("InitialBaseCenter", out var initialBaseCenterNode)) + initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value); + + if (nodes.TryGetValue("UnitsHangingAroundTheBase", out var unitsHangingAroundTheBaseNode)) { unitsHangingAroundTheBase.Clear(); - unitsHangingAroundTheBase.AddRange(FieldLoader.GetValue("UnitsHangingAroundTheBase", unitsHangingAroundTheBaseNode.Value.Value) + unitsHangingAroundTheBase.AddRange(FieldLoader.GetValue("UnitsHangingAroundTheBase", unitsHangingAroundTheBaseNode.Value) .Select(a => self.World.GetActorById(a)).Where(a => a != null)); } - var activeUnitsNode = data.FirstOrDefault(n => n.Key == "ActiveUnits"); - if (activeUnitsNode != null) + if (nodes.TryGetValue("ActiveUnits", out var activeUnitsNode)) { activeUnits.Clear(); - activeUnits.UnionWith(FieldLoader.GetValue("ActiveUnits", activeUnitsNode.Value.Value) + activeUnits.UnionWith(FieldLoader.GetValue("ActiveUnits", activeUnitsNode.Value) .Select(a => self.World.GetActorById(a)).Where(a => a != null)); } - var rushTicksNode = data.FirstOrDefault(n => n.Key == "RushTicks"); - if (rushTicksNode != null) - rushTicks = FieldLoader.GetValue("RushTicks", rushTicksNode.Value.Value); + if (nodes.TryGetValue("RushTicks", out var rushTicksNode)) + rushTicks = FieldLoader.GetValue("RushTicks", rushTicksNode.Value); - var assignRolesTicksNode = data.FirstOrDefault(n => n.Key == "AssignRolesTicks"); - if (assignRolesTicksNode != null) - assignRolesTicks = FieldLoader.GetValue("AssignRolesTicks", assignRolesTicksNode.Value.Value); + if (nodes.TryGetValue("AssignRolesTicks", out var assignRolesTicksNode)) + assignRolesTicks = FieldLoader.GetValue("AssignRolesTicks", assignRolesTicksNode.Value); - var attackForceTicksNode = data.FirstOrDefault(n => n.Key == "AttackForceTicks"); - if (attackForceTicksNode != null) - attackForceTicks = FieldLoader.GetValue("AttackForceTicks", attackForceTicksNode.Value.Value); + if (nodes.TryGetValue("AttackForceTicks", out var attackForceTicksNode)) + attackForceTicks = FieldLoader.GetValue("AttackForceTicks", attackForceTicksNode.Value); - var minAttackForceDelayTicksNode = data.FirstOrDefault(n => n.Key == "MinAttackForceDelayTicks"); - if (minAttackForceDelayTicksNode != null) - minAttackForceDelayTicks = FieldLoader.GetValue("MinAttackForceDelayTicks", minAttackForceDelayTicksNode.Value.Value); + if (nodes.TryGetValue("MinAttackForceDelayTicks", out var minAttackForceDelayTicksNode)) + minAttackForceDelayTicks = FieldLoader.GetValue("MinAttackForceDelayTicks", minAttackForceDelayTicksNode.Value); - var squadsNode = data.FirstOrDefault(n => n.Key == "Squads"); - if (squadsNode != null) + if (nodes.TryGetValue("Squads", out var squadsNode)) { Squads.Clear(); - foreach (var n in squadsNode.Value.Nodes) + foreach (var n in squadsNode.Nodes) Squads.Add(Squad.Deserialize(bot, this, n.Value)); } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs index 1b5f632be6..7afe09e31b 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs @@ -155,12 +155,12 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads var type = SquadType.Rush; var target = ((Actor)null, WVec.Zero); - var typeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type"); + var typeNode = yaml.NodeWithKeyOrDefault("Type"); if (typeNode != null) type = FieldLoader.GetValue("Type", typeNode.Value.Value); - var actorToTargetNode = yaml.Nodes.FirstOrDefault(n => n.Key == "ActorToTarget"); - var targetOffsetNode = yaml.Nodes.FirstOrDefault(n => n.Key == "TargetOffset"); + var actorToTargetNode = yaml.NodeWithKeyOrDefault("ActorToTarget"); + var targetOffsetNode = yaml.NodeWithKeyOrDefault("TargetOffset"); if (actorToTargetNode != null && targetOffsetNode != null) { var actorToTarget = squadManager.World.GetActorById(FieldLoader.GetValue("ActorToTarget", actorToTargetNode.Value.Value)); @@ -170,7 +170,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads var squad = new Squad(bot, squadManager, type, target); - var unitsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Units"); + var unitsNode = yaml.NodeWithKeyOrDefault("Units"); if (unitsNode != null) squad.Units.UnionWith(FieldLoader.GetValue("Units", unitsNode.Value.Value) .Select(a => squadManager.World.GetActorById(a))); diff --git a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs index fe94e6f73e..079fc3c7fd 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -27,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits static object LoadDecisions(MiniYaml yaml) { var ret = new List(); - var decisions = yaml.Nodes.FirstOrDefault(n => n.Key == "Decisions"); + var decisions = yaml.NodeWithKeyOrDefault("Decisions"); if (decisions != null) foreach (var d in decisions.Value.Nodes) ret.Add(new SupportPowerDecision(d.Value)); @@ -224,12 +223,12 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { if (self.World.IsReplay) return; - var waitingPowersNode = data.FirstOrDefault(n => n.Key == "WaitingPowers"); + var waitingPowersNode = data.NodeWithKeyOrDefault("WaitingPowers"); if (waitingPowersNode != null) { foreach (var n in waitingPowersNode.Value.Nodes) diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index 0d576c6e3f..7964b320f4 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -234,19 +233,19 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { if (self.World.IsReplay) return; - var queuedBuildRequestsNode = data.FirstOrDefault(n => n.Key == "QueuedBuildRequests"); + var queuedBuildRequestsNode = data.NodeWithKeyOrDefault("QueuedBuildRequests"); if (queuedBuildRequestsNode != null) { queuedBuildRequests.Clear(); queuedBuildRequests.AddRange(FieldLoader.GetValue("QueuedBuildRequests", queuedBuildRequestsNode.Value.Value)); } - var idleUnitCountNode = data.FirstOrDefault(n => n.Key == "IdleUnitCount"); + var idleUnitCountNode = data.NodeWithKeyOrDefault("IdleUnitCount"); if (idleUnitCountNode != null) idleUnitCount = FieldLoader.GetValue("IdleUnitCount", idleUnitCountNode.Value.Value); } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index ab14327a67..bf037e72d6 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -64,10 +64,10 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadFootprint(MiniYaml yaml) { - var footprintYaml = yaml.Nodes.FirstOrDefault(n => n.Key == "Footprint"); + var footprintYaml = yaml.NodeWithKeyOrDefault("Footprint"); var footprintChars = footprintYaml?.Value.Value.Where(x => !char.IsWhiteSpace(x)).ToArray() ?? new[] { 'x' }; - var dimensionsYaml = yaml.Nodes.FirstOrDefault(n => n.Key == "Dimensions"); + var dimensionsYaml = yaml.NodeWithKeyOrDefault("Dimensions"); var dim = dimensionsYaml != null ? FieldLoader.GetValue("Dimensions", dimensionsYaml.Value.Value) : new CVec(1, 1); if (footprintChars.Length != dim.X * dim.Y) diff --git a/OpenRA.Mods.Common/Traits/HitShape.cs b/OpenRA.Mods.Common/Traits/HitShape.cs index 467c20ea99..00c5039fcf 100644 --- a/OpenRA.Mods.Common/Traits/HitShape.cs +++ b/OpenRA.Mods.Common/Traits/HitShape.cs @@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits { IHitShape ret; - var shapeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type"); + var shapeNode = yaml.NodeWithKeyOrDefault("Type"); var shape = shapeNode != null ? shapeNode.Value.Value : string.Empty; if (!string.IsNullOrEmpty(shape)) diff --git a/OpenRA.Mods.Common/Traits/Player/GameSaveViewportManager.cs b/OpenRA.Mods.Common/Traits/Player/GameSaveViewportManager.cs index 7f01f56347..6210d68578 100644 --- a/OpenRA.Mods.Common/Traits/Player/GameSaveViewportManager.cs +++ b/OpenRA.Mods.Common/Traits/Player/GameSaveViewportManager.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -50,13 +49,13 @@ namespace OpenRA.Mods.Common.Traits return nodes; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { - var viewportNode = data.FirstOrDefault(n => n.Key == "Viewport"); + var viewportNode = data.NodeWithKeyOrDefault("Viewport"); if (viewportNode != null) worldRenderer.Viewport.Center(FieldLoader.GetValue("Viewport", viewportNode.Value.Value)); - var renderPlayerNode = data.FirstOrDefault(n => n.Key == "RenderPlayer"); + var renderPlayerNode = data.NodeWithKeyOrDefault("RenderPlayer"); if (renderPlayerNode != null) { var renderPlayerActorID = FieldLoader.GetValue("RenderPlayer", renderPlayerNode.Value.Value); diff --git a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs index 3579b7d1a3..9022d37d30 100644 --- a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs +++ b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs @@ -133,9 +133,9 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { - var groupsNode = data.FirstOrDefault(n => n.Key == "Groups"); + var groupsNode = data.NodeWithKeyOrDefault("Groups"); if (groupsNode != null) { foreach (var n in groupsNode.Value.Nodes) diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index d9f16b6ac0..aa060ae850 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadResourceTypes(MiniYaml yaml) { var ret = new Dictionary(); - var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes"); + var resources = yaml.NodeWithKeyOrDefault("ResourceTypes"); if (resources != null) foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceLayerInfo.ResourceTypeInfo(r.Value); diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index 2d14b273a5..53c99f2849 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -84,16 +84,16 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadSpeeds(MiniYaml y) { - var speeds = y.ToDictionary()["TerrainSpeeds"].Nodes; + var speeds = y.NodeWithKey("TerrainSpeeds").Value.Nodes; var ret = new Dictionary(speeds.Length); foreach (var t in speeds) { var speed = FieldLoader.GetValue("speed", t.Value.Value); if (speed > 0) { - var nodesDict = t.Value.ToDictionary(); - var cost = nodesDict.TryGetValue("PathingCost", out var entry) - ? FieldLoader.GetValue("cost", entry.Value) + var pathingCost = t.Value.NodeWithKeyOrDefault("PathingCost"); + var cost = pathingCost != null + ? FieldLoader.GetValue("cost", pathingCost.Value.Value) : 10000 / speed; ret.Add(t.Key, new TerrainInfo(speed, (short)cost)); } diff --git a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs index 498eedee5f..5266eb40e3 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs @@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadResourceTypes(MiniYaml yaml) { var ret = new Dictionary(); - var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes"); + var resources = yaml.NodeWithKeyOrDefault("ResourceTypes"); if (resources != null) foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceTypeInfo(r.Value); diff --git a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs index e05548a798..0a79f976b3 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs @@ -54,7 +54,7 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadResourceTypes(MiniYaml yaml) { var ret = new Dictionary(); - var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes"); + var resources = yaml.NodeWithKeyOrDefault("ResourceTypes"); if (resources != null) foreach (var r in resources.Value.Nodes) ret[r.Key] = new ResourceTypeInfo(r.Value); diff --git a/OpenRA.Mods.Common/Traits/World/Selection.cs b/OpenRA.Mods.Common/Traits/World/Selection.cs index 64e081cf1e..22c51cf905 100644 --- a/OpenRA.Mods.Common/Traits/World/Selection.cs +++ b/OpenRA.Mods.Common/Traits/World/Selection.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using OpenRA.Traits; @@ -186,9 +185,9 @@ namespace OpenRA.Mods.Common.Traits }; } - void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray data) + void IGameSaveTraitData.ResolveTraitData(Actor self, MiniYaml data) { - var selectionNode = data.FirstOrDefault(n => n.Key == "Selection"); + var selectionNode = data.NodeWithKeyOrDefault("Selection"); if (selectionNode != null) { var selected = FieldLoader.GetValue("Selection", selectionNode.Value.Value) diff --git a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs index 9b830c6fe2..6ea51afb84 100644 --- a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs @@ -56,11 +56,11 @@ namespace OpenRA.Mods.Common.Traits public static object LoadInitialSmudges(MiniYaml yaml) { - var nd = yaml.ToDictionary(); var smudges = new Dictionary(); - if (nd.TryGetValue("InitialSmudges", out var smudgeYaml)) + var smudgeYaml = yaml.NodeWithKeyOrDefault("InitialSmudges"); + if (smudgeYaml != null) { - foreach (var node in smudgeYaml.Nodes) + foreach (var node in smudgeYaml.Value.Nodes) { try { diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs index 6a42628fa8..69ea3a2b64 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs @@ -230,7 +230,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules if (!string.IsNullOrEmpty(sequenceNode.Value.Value)) explicitlyNamedSequences.Add(sequenceNode.Key); - var resolvedSequenceNode = resolvedImageNode.Value.Nodes.SingleOrDefault(n => n.Key == sequenceNode.Key); + var resolvedSequenceNode = resolvedImageNode.Value.NodeWithKeyOrDefault(sequenceNode.Key); if (resolvedSequenceNode == null) continue; diff --git a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs index 76431011dc..86fc35db20 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdateUtils.cs @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.UpdateRules manualSteps.AddRange(rule.BeforeUpdate(modData)); - var mapActorsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Actors"); + var mapActorsNode = yaml.NodeWithKeyOrDefault("Actors"); if (mapActorsNode != null) { var mapActors = new YamlFileSet() @@ -111,7 +111,7 @@ namespace OpenRA.Mods.Common.UpdateRules files.AddRange(mapActors); } - var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); + var mapRulesNode = yaml.NodeWithKeyOrDefault("Rules"); if (mapRulesNode != null) { if (rule is IBeforeUpdateActors before) @@ -125,7 +125,7 @@ namespace OpenRA.Mods.Common.UpdateRules files.AddRange(mapRules); } - var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); + var mapWeaponsNode = yaml.NodeWithKeyOrDefault("Weapons"); if (mapWeaponsNode != null) { if (rule is IBeforeUpdateWeapons before) @@ -139,7 +139,7 @@ namespace OpenRA.Mods.Common.UpdateRules files.AddRange(mapWeapons); } - var mapSequencesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Sequences"); + var mapSequencesNode = yaml.NodeWithKeyOrDefault("Sequences"); if (mapSequencesNode != null) { if (rule is IBeforeUpdateSequences before) @@ -216,19 +216,19 @@ namespace OpenRA.Mods.Common.UpdateRules continue; var yaml = new MiniYamlBuilder(new MiniYaml(null, MiniYaml.FromStream(mapStream, package.Name, false))); - var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); + var mapRulesNode = yaml.NodeWithKeyOrDefault("Rules"); if (mapRulesNode != null) foreach (var f in LoadExternalMapYaml(modData, mapRulesNode.Value, externalFilenames)) if (!modRules.Any(m => m.Item1 == f.Item1 && m.Item2 == f.Item2)) modRules.Add(f); - var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons"); + var mapWeaponsNode = yaml.NodeWithKeyOrDefault("Weapons"); if (mapWeaponsNode != null) foreach (var f in LoadExternalMapYaml(modData, mapWeaponsNode.Value, externalFilenames)) if (!modWeapons.Any(m => m.Item1 == f.Item1 && m.Item2 == f.Item2)) modWeapons.Add(f); - var mapSequencesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Sequences"); + var mapSequencesNode = yaml.NodeWithKeyOrDefault("Sequences"); if (mapSequencesNode != null) foreach (var f in LoadExternalMapYaml(modData, mapSequencesNode.Value, externalFilenames)) if (!modSequences.Any(m => m.Item1 == f.Item1 && m.Item2 == f.Item2)) @@ -285,7 +285,7 @@ namespace OpenRA.Mods.Common.UpdateRules foreach (var manualStep in transform(modData, current)) yield return manualStep; - var childrenNode = current.Value.Nodes.FirstOrDefault(n => n.Key == "Children"); + var childrenNode = current.Value.NodeWithKeyOrDefault("Children"); if (childrenNode != null) foreach (var node in childrenNode.Value.Nodes) if (node.Key != null) diff --git a/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs b/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs index 17e7f82f98..c548a230e5 100644 --- a/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ControlGroupsWidget.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Lint; using OpenRA.Traits; @@ -47,27 +46,27 @@ namespace OpenRA.Mods.Common.Widgets yield break; var selectPrefix = ""; - var selectPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "SelectGroupKeyPrefix"); + var selectPrefixNode = widgetNode.Value.NodeWithKeyOrDefault("SelectGroupKeyPrefix"); if (selectPrefixNode != null) selectPrefix = selectPrefixNode.Value.Value; var createPrefix = ""; - var createPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "CreateGroupKeyPrefix"); + var createPrefixNode = widgetNode.Value.NodeWithKeyOrDefault("CreateGroupKeyPrefix"); if (createPrefixNode != null) createPrefix = createPrefixNode.Value.Value; var addToPrefix = ""; - var addToPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "AddToGroupKeyPrefix"); + var addToPrefixNode = widgetNode.Value.NodeWithKeyOrDefault("AddToGroupKeyPrefix"); if (addToPrefixNode != null) addToPrefix = addToPrefixNode.Value.Value; var combineWithPrefix = ""; - var combineWithPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "CombineWithGroupKeyPrefix"); + var combineWithPrefixNode = widgetNode.Value.NodeWithKeyOrDefault("CombineWithGroupKeyPrefix"); if (combineWithPrefixNode != null) combineWithPrefix = combineWithPrefixNode.Value.Value; var jumpToPrefix = ""; - var jumpToPrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "JumpToGroupKeyPrefix"); + var jumpToPrefixNode = widgetNode.Value.NodeWithKeyOrDefault("JumpToGroupKeyPrefix"); if (jumpToPrefixNode != null) jumpToPrefix = jumpToPrefixNode.Value.Value; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs index aabf39b7a3..e5ff34bbc4 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs @@ -246,7 +246,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { void RunSourceActions(MiniYamlNode contentPackageYaml) { - var sourceActionListYaml = contentPackageYaml.Value.Nodes.FirstOrDefault(x => x.Key == "Actions"); + var sourceActionListYaml = contentPackageYaml.Value.NodeWithKeyOrDefault("Actions"); if (sourceActionListYaml == null) return; @@ -263,7 +263,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic foreach (var packageInstallationNode in modSource.Install.Where(x => x.Key == "ContentPackage")) { - var packageName = packageInstallationNode.Value.Nodes.SingleOrDefault(x => x.Key == "Name")?.Value.Value; + var packageName = packageInstallationNode.Value.NodeWithKeyOrDefault("Name")?.Value.Value; if (!string.IsNullOrEmpty(packageName) && selectedPackages.TryGetValue(packageName, out var required) && required) RunSourceActions(packageInstallationNode); } @@ -338,9 +338,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic checkboxWidget.OnClick = () => selectedPackages[package.Identifier] = !selectedPackages[package.Identifier]; var contentPackageNode = source.Install.FirstOrDefault(x => - x.Value.Nodes.FirstOrDefault(y => y.Key == "Name")?.Value.Value == package.Identifier); + x.Value.NodeWithKeyOrDefault("Name")?.Value.Value == package.Identifier); - var tooltipText = contentPackageNode?.Value.Nodes.FirstOrDefault(x => x.Key == nameof(ModContent.ModSource.TooltipText))?.Value.Value; + var tooltipText = contentPackageNode?.Value.NodeWithKeyOrDefault(nameof(ModContent.ModSource.TooltipText))?.Value.Value; var tooltipIcon = containerWidget.Get("PACKAGE_INFO"); tooltipIcon.IsVisible = () => !string.IsNullOrWhiteSpace(tooltipText); tooltipIcon.GetTooltipText = () => tooltipText; diff --git a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs index 445791c800..18581affab 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs @@ -487,7 +487,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic continue; var game = new MiniYamlBuilder(MiniYaml.FromString(bl.Data)[0].Value); - var idNode = game.Nodes.FirstOrDefault(n => n.Key == "Id"); + var idNode = game.NodeWithKeyOrDefault("Id"); // Skip beacons created by this instance and replace Id by expected int value if (idNode != null && idNode.Value.Value != Platform.SessionGUID.ToString()) @@ -495,7 +495,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic idNode.Value.Value = "-1"; // Rewrite the server address with the correct IP - var addressNode = game.Nodes.FirstOrDefault(n => n.Key == "Address"); + var addressNode = game.NodeWithKeyOrDefault("Address"); if (addressNode != null) addressNode.Value.Value = bl.Address.ToString().Split(':')[0] + ":" + addressNode.Value.Value.Split(':')[1]; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs index 561f8f991e..154fd98546 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs @@ -138,7 +138,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { foreach (var hg in hotkeyGroupsYaml.Nodes) { - var typesNode = hg.Value.Nodes.FirstOrDefault(n => n.Key == "Types"); + var typesNode = hg.Value.NodeWithKeyOrDefault("Types"); if (typesNode != null) hotkeyGroups.Add(hg.Key, FieldLoader.GetValue>("Types", typesNode.Value.Value)); } diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index 66edfbd2b2..d63a3af8bb 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -132,12 +132,12 @@ namespace OpenRA.Mods.Common.Widgets public static IEnumerable LinterHotkeyNames(MiniYamlNode widgetNode, Action emitError) { var prefix = ""; - var prefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "HotkeyPrefix"); + var prefixNode = widgetNode.Value.NodeWithKeyOrDefault("HotkeyPrefix"); if (prefixNode != null) prefix = prefixNode.Value.Value; var count = 0; - var countNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "HotkeyCount"); + var countNode = widgetNode.Value.NodeWithKeyOrDefault("HotkeyCount"); if (countNode != null) count = FieldLoader.GetValue("HotkeyCount", countNode.Value.Value); diff --git a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs index f44c1869dd..2dc70f7364 100644 --- a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs @@ -70,12 +70,12 @@ namespace OpenRA.Mods.Common.Widgets public static IEnumerable LinterHotkeyNames(MiniYamlNode widgetNode, Action emitError) { var prefix = ""; - var prefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "HotkeyPrefix"); + var prefixNode = widgetNode.Value.NodeWithKeyOrDefault("HotkeyPrefix"); if (prefixNode != null) prefix = prefixNode.Value.Value; var count = 0; - var countNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "HotkeyCount"); + var countNode = widgetNode.Value.NodeWithKeyOrDefault("HotkeyCount"); if (countNode != null) count = FieldLoader.GetValue("HotkeyCount", countNode.Value.Value); diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index 7da08b1fb2..61b184d80d 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -105,17 +105,17 @@ namespace OpenRA.Mods.Common.Widgets public static IEnumerable LinterHotkeyNames(MiniYamlNode widgetNode, Action emitError) { var savePrefix = ""; - var savePrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "BookmarkSaveKeyPrefix"); + var savePrefixNode = widgetNode.Value.NodeWithKeyOrDefault("BookmarkSaveKeyPrefix"); if (savePrefixNode != null) savePrefix = savePrefixNode.Value.Value; var restorePrefix = ""; - var restorePrefixNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "BookmarkRestoreKeyPrefix"); + var restorePrefixNode = widgetNode.Value.NodeWithKeyOrDefault("BookmarkRestoreKeyPrefix"); if (restorePrefixNode != null) restorePrefix = restorePrefixNode.Value.Value; var count = 0; - var countNode = widgetNode.Value.Nodes.FirstOrDefault(n => n.Key == "BookmarkKeyCount"); + var countNode = widgetNode.Value.NodeWithKeyOrDefault("BookmarkKeyCount"); if (countNode != null) count = FieldLoader.GetValue("BookmarkKeyCount", countNode.Value.Value); diff --git a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs index 462b733880..70d19fdbd2 100644 --- a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs +++ b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs @@ -384,7 +384,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands actorNodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + actorNodes.Count), a.Save())); if (map.PlayerDefinitions.Concat(playerNodes).All( - x => x.Value.Nodes.Single(y => y.Key == "Name").Value.Value != kvp.Owner)) + x => x.Value.NodeWithKey("Name").Value.Value != kvp.Owner)) { var playerInfo = PlayerReferenceDataByPlayerName[kvp.Owner]; var playerReference = new PlayerReference diff --git a/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs b/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs index c9b223b276..4223eca9cc 100644 --- a/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs +++ b/OpenRA.Test/OpenRA.Game/MiniYamlTest.cs @@ -247,7 +247,7 @@ Test: .First(n => n.Key == "Test").Value.Nodes; Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not."); - Assert.IsTrue(result.First(n => n.Key == "MockString").Value.ToDictionary()["AString"].Value == "Override", + Assert.IsTrue(result.First(n => n.Key == "MockString").Value.NodeWithKey("AString").Value.Value == "Override", "MockString value has not been set with the correct override value for AString."); } @@ -272,7 +272,7 @@ Test: .First(n => n.Key == "Test").Value.Nodes; Assert.IsTrue(result.Any(n => n.Key == "MockString"), "Node should have the MockString child, but does not."); - Assert.IsTrue(result.First(n => n.Key == "MockString").Value.ToDictionary()["AString"].Value == "Override", + Assert.IsTrue(result.First(n => n.Key == "MockString").Value.NodeWithKey("AString").Value.Value == "Override", "MockString value has not been set with the correct override value for AString."); } diff --git a/mods/ts/sequences/civilian.yaml b/mods/ts/sequences/civilian.yaml index 8e5aaa9cd5..d6903b3f0a 100644 --- a/mods/ts/sequences/civilian.yaml +++ b/mods/ts/sequences/civilian.yaml @@ -779,7 +779,6 @@ ctdam: damaged-idle-water-b: Filename: ctdam_b.shp Start: 24 - Start: 8 Length: 8 Tick: 200 diff --git a/mods/ts/sequences/structures.yaml b/mods/ts/sequences/structures.yaml index acc008a3c8..3cd244ed7f 100644 --- a/mods/ts/sequences/structures.yaml +++ b/mods/ts/sequences/structures.yaml @@ -1667,7 +1667,6 @@ napuls.nod: ZOffset: 512 BlendMode: Additive IgnoreWorldTint: True - IgnoreWorldTint: True DepthSprite: icon: Filename: sidebar-nod|empicon.shp