MiniYaml becomes an immutable data structure.

This changeset is motivated by a simple concept - get rid of the MiniYaml.Clone and MiniYamlNode.Clone methods to avoid deep copying yaml trees during merging. MiniYaml becoming immutable allows the merge function to reuse existing yaml trees rather than cloning them, saving on memory and improving merge performance. On initial loading the YAML for all maps is processed, so this provides a small reduction in initial loading time.

The rest of the changeset is dealing with the change in the exposed API surface. Some With* helper methods are introduced to allow creating new YAML from existing YAML. Areas of code that generated small amounts of YAML are able to transition directly to the immutable model without too much ceremony. Some use cases are far less ergonomic even with these helper methods and so a MiniYamlBuilder is introduced to retain mutable creation functionality. This allows those areas to continue to use the old mutable structures. The main users are the update rules and linting capabilities.
This commit is contained in:
RoosterDragon
2023-05-06 10:39:15 +01:00
committed by Gustas
parent b6a5d19871
commit 949ba589c0
104 changed files with 722 additions and 393 deletions

View File

@@ -122,7 +122,7 @@ namespace OpenRA
return; return;
var key = ExternalMod.MakeKey(mod); var key = ExternalMod.MakeKey(mod);
var yaml = new MiniYamlNode("Registration", new MiniYaml("", new List<MiniYamlNode>() var yaml = new MiniYamlNode("Registration", new MiniYaml("", new[]
{ {
new MiniYamlNode("Id", mod.Id), new MiniYamlNode("Id", mod.Id),
new MiniYamlNode("Version", mod.Metadata.Version), new MiniYamlNode("Version", mod.Metadata.Version),
@@ -131,17 +131,21 @@ namespace OpenRA
new MiniYamlNode("LaunchArgs", new[] { "Game.Mod=" + mod.Id }.Concat(launchArgs).JoinWith(", ")) new MiniYamlNode("LaunchArgs", new[] { "Game.Mod=" + mod.Id }.Concat(launchArgs).JoinWith(", "))
})); }));
var iconNodes = new List<MiniYamlNode>();
using (var stream = mod.Package.GetStream("icon.png")) using (var stream = mod.Package.GetStream("icon.png"))
if (stream != null) if (stream != null)
yaml.Value.Nodes.Add(new MiniYamlNode("Icon", Convert.ToBase64String(stream.ReadAllBytes()))); iconNodes.Add(new MiniYamlNode("Icon", Convert.ToBase64String(stream.ReadAllBytes())));
using (var stream = mod.Package.GetStream("icon-2x.png")) using (var stream = mod.Package.GetStream("icon-2x.png"))
if (stream != null) if (stream != null)
yaml.Value.Nodes.Add(new MiniYamlNode("Icon2x", Convert.ToBase64String(stream.ReadAllBytes()))); iconNodes.Add(new MiniYamlNode("Icon2x", Convert.ToBase64String(stream.ReadAllBytes())));
using (var stream = mod.Package.GetStream("icon-3x.png")) using (var stream = mod.Package.GetStream("icon-3x.png"))
if (stream != null) if (stream != null)
yaml.Value.Nodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes()))); iconNodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes())));
yaml = yaml.WithValue(yaml.Value.WithNodesAppended(iconNodes));
var sources = new HashSet<string>(); var sources = new HashSet<string>();
if (registration.HasFlag(ModRegistration.System)) if (registration.HasFlag(ModRegistration.System))

View File

@@ -500,7 +500,7 @@ namespace OpenRA
if (yaml == null) if (yaml == null)
return Activator.CreateInstance(fieldType); return Activator.CreateInstance(fieldType);
var dict = Activator.CreateInstance(fieldType, yaml.Nodes.Count); var dict = Activator.CreateInstance(fieldType, yaml.Nodes.Length);
var arguments = fieldType.GetGenericArguments(); var arguments = fieldType.GetGenericArguments();
var addMethod = fieldType.GetMethod(nameof(Dictionary<object, object>.Add), arguments); var addMethod = fieldType.GetMethod(nameof(Dictionary<object, object>.Add), arguments);
var addArgs = new object[2]; var addArgs = new object[2];

View File

@@ -58,7 +58,7 @@ namespace OpenRA
return new MiniYaml( return new MiniYaml(
null, null,
fields.Select(info => new MiniYamlNode(info.YamlName, FormatValue(o, info.Field))).ToList()); fields.Select(info => new MiniYamlNode(info.YamlName, FormatValue(o, info.Field))));
} }
public static MiniYamlNode SaveField(object o, string field) public static MiniYamlNode SaveField(object o, string field)

View File

@@ -226,10 +226,10 @@ namespace OpenRA
static bool AnyCustomYaml(MiniYaml yaml) static bool AnyCustomYaml(MiniYaml yaml)
{ {
return yaml != null && (yaml.Value != null || yaml.Nodes.Count > 0); return yaml != null && (yaml.Value != null || yaml.Nodes.Length > 0);
} }
static bool AnyFlaggedTraits(ModData modData, List<MiniYamlNode> actors) static bool AnyFlaggedTraits(ModData modData, IEnumerable<MiniYamlNode> actors)
{ {
foreach (var actorNode in actors) foreach (var actorNode in actors)
{ {

View File

@@ -139,7 +139,7 @@ namespace OpenRA.GameRules
{ {
// Resolve any weapon-level yaml inheritance or removals // Resolve any weapon-level yaml inheritance or removals
// HACK: The "Defaults" sequence syntax prevents us from doing this generally during yaml parsing // HACK: The "Defaults" sequence syntax prevents us from doing this generally during yaml parsing
content.Nodes = MiniYaml.Merge(new[] { content.Nodes }); content = content.WithNodes(MiniYaml.Merge(new IReadOnlyCollection<MiniYamlNode>[] { content.Nodes }));
FieldLoader.Load(this, content); FieldLoader.Load(this, content);
} }

View File

@@ -84,7 +84,7 @@ namespace OpenRA
public MiniYaml Save(Func<ActorInit, bool> initFilter = null) public MiniYaml Save(Func<ActorInit, bool> initFilter = null)
{ {
var ret = new MiniYaml(Type); var nodes = new List<MiniYamlNode>();
foreach (var o in initDict.Value) foreach (var o in initDict.Value)
{ {
if (o is not ActorInit init || o is ISuppressInitExport) if (o is not ActorInit init || o is ISuppressInitExport)
@@ -98,10 +98,10 @@ namespace OpenRA
if (!string.IsNullOrEmpty(init.InstanceName)) if (!string.IsNullOrEmpty(init.InstanceName))
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName; initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
ret.Nodes.Add(new MiniYamlNode(initName, init.Save())); nodes.Add(new MiniYamlNode(initName, init.Save()));
} }
return ret; return new MiniYaml(Type, nodes);
} }
public IEnumerator<object> GetEnumerator() { return initDict.Value.GetEnumerator(); } public IEnumerator<object> GetEnumerator() { return initDict.Value.GetEnumerator(); }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -90,11 +91,11 @@ namespace OpenRA
throw new InvalidOperationException("Map does not have a field/property " + fieldName); throw new InvalidOperationException("Map does not have a field/property " + fieldName);
var t = field != null ? field.FieldType : property.PropertyType; var t = field != null ? field.FieldType : property.PropertyType;
type = t == typeof(List<MiniYamlNode>) ? Type.NodeList : type = t == typeof(IReadOnlyCollection<MiniYamlNode>) ? Type.NodeList :
t == typeof(MiniYaml) ? Type.MiniYaml : Type.Normal; t == typeof(MiniYaml) ? Type.MiniYaml : Type.Normal;
} }
public void Deserialize(Map map, List<MiniYamlNode> nodes) public void Deserialize(Map map, ImmutableArray<MiniYamlNode> nodes)
{ {
var node = nodes.FirstOrDefault(n => n.Key == key); var node = nodes.FirstOrDefault(n => n.Key == key);
if (node == null) if (node == null)
@@ -130,14 +131,14 @@ namespace OpenRA
var value = field != null ? field.GetValue(map) : property.GetValue(map, null); var value = field != null ? field.GetValue(map) : property.GetValue(map, null);
if (type == Type.NodeList) if (type == Type.NodeList)
{ {
var listValue = (List<MiniYamlNode>)value; var listValue = (IReadOnlyCollection<MiniYamlNode>)value;
if (required || listValue.Count > 0) if (required || listValue.Count > 0)
nodes.Add(new MiniYamlNode(key, null, listValue)); nodes.Add(new MiniYamlNode(key, null, listValue));
} }
else if (type == Type.MiniYaml) else if (type == Type.MiniYaml)
{ {
var yamlValue = (MiniYaml)value; var yamlValue = (MiniYaml)value;
if (required || (yamlValue != null && (yamlValue.Value != null || yamlValue.Nodes.Count > 0))) if (required || (yamlValue != null && (yamlValue.Value != null || yamlValue.Nodes.Length > 0)))
nodes.Add(new MiniYamlNode(key, yamlValue)); nodes.Add(new MiniYamlNode(key, yamlValue));
} }
else else
@@ -197,18 +198,18 @@ namespace OpenRA
public int2 MapSize { get; private set; } public int2 MapSize { get; private set; }
// Player and actor yaml. Public for access by the map importers and lint checks. // Player and actor yaml. Public for access by the map importers and lint checks.
public List<MiniYamlNode> PlayerDefinitions = new(); public IReadOnlyCollection<MiniYamlNode> PlayerDefinitions = ImmutableArray<MiniYamlNode>.Empty;
public List<MiniYamlNode> ActorDefinitions = new(); public IReadOnlyCollection<MiniYamlNode> ActorDefinitions = ImmutableArray<MiniYamlNode>.Empty;
// Custom map yaml. Public for access by the map importers and lint checks // Custom map yaml. Public for access by the map importers and lint checks
public readonly MiniYaml RuleDefinitions; public MiniYaml RuleDefinitions;
public readonly MiniYaml TranslationDefinitions; public MiniYaml TranslationDefinitions;
public readonly MiniYaml SequenceDefinitions; public MiniYaml SequenceDefinitions;
public readonly MiniYaml ModelSequenceDefinitions; public MiniYaml ModelSequenceDefinitions;
public readonly MiniYaml WeaponDefinitions; public MiniYaml WeaponDefinitions;
public readonly MiniYaml VoiceDefinitions; public MiniYaml VoiceDefinitions;
public readonly MiniYaml MusicDefinitions; public MiniYaml MusicDefinitions;
public readonly MiniYaml NotificationDefinitions; public MiniYaml NotificationDefinitions;
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new(); public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new();

View File

@@ -142,7 +142,7 @@ namespace OpenRA
var sources = var sources =
modDataRules.Select(x => x.Where(IsLoadableRuleDefinition).ToList()) modDataRules.Select(x => x.Where(IsLoadableRuleDefinition).ToList())
.Concat(files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList())); .Concat(files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList()));
if (RuleDefinitions.Nodes.Count > 0) if (RuleDefinitions.Nodes.Length > 0)
sources = sources.Append(RuleDefinitions.Nodes.Where(IsLoadableRuleDefinition).ToList()); sources = sources.Append(RuleDefinitions.Nodes.Where(IsLoadableRuleDefinition).ToList());
var yamlNodes = MiniYaml.Merge(sources); var yamlNodes = MiniYaml.Merge(sources);

View File

@@ -20,18 +20,36 @@ namespace OpenRA
{ {
public static class MiniYamlExts public static class MiniYamlExts
{ {
public static void WriteToFile(this List<MiniYamlNode> y, string filename) public static void WriteToFile(this IEnumerable<MiniYamlNode> y, string filename)
{ {
File.WriteAllLines(filename, y.ToLines().Select(x => x.TrimEnd()).ToArray()); File.WriteAllLines(filename, y.ToLines().Select(x => x.TrimEnd()).ToArray());
} }
public static string WriteToString(this List<MiniYamlNode> y) public static string WriteToString(this IEnumerable<MiniYamlNode> y)
{ {
// Remove all trailing newlines and restore the final EOF newline // Remove all trailing newlines and restore the final EOF newline
return y.ToLines().JoinWith("\n").TrimEnd('\n') + "\n"; return y.ToLines().JoinWith("\n").TrimEnd('\n') + "\n";
} }
public static IEnumerable<string> ToLines(this List<MiniYamlNode> y) public static IEnumerable<string> ToLines(this IEnumerable<MiniYamlNode> y)
{
foreach (var kv in y)
foreach (var line in kv.Value.ToLines(kv.Key, kv.Comment))
yield return line;
}
public static void WriteToFile(this IEnumerable<MiniYamlNodeBuilder> y, string filename)
{
File.WriteAllLines(filename, y.ToLines().Select(x => x.TrimEnd()).ToArray());
}
public static string WriteToString(this IEnumerable<MiniYamlNodeBuilder> y)
{
// Remove all trailing newlines and restore the final EOF newline
return y.ToLines().JoinWith("\n").TrimEnd('\n') + "\n";
}
public static IEnumerable<string> ToLines(this IEnumerable<MiniYamlNodeBuilder> y)
{ {
foreach (var kv in y) foreach (var kv in y)
foreach (var line in kv.Value.ToLines(kv.Key, kv.Comment)) foreach (var line in kv.Value.ToLines(kv.Key, kv.Comment))
@@ -55,10 +73,17 @@ namespace OpenRA
public override string ToString() { return $"{Filename}:{Line}"; } public override string ToString() { return $"{Filename}:{Line}"; }
} }
public SourceLocation Location; public readonly SourceLocation Location;
public string Key; public readonly string Key;
public MiniYaml Value; public readonly MiniYaml Value;
public string Comment; public readonly string Comment;
public MiniYamlNode WithValue(MiniYaml value)
{
if (Value == value)
return this;
return new MiniYamlNode(Key, value, Comment, Location);
}
public MiniYamlNode(string k, MiniYaml v, string c = null) public MiniYamlNode(string k, MiniYaml v, string c = null)
{ {
@@ -74,26 +99,15 @@ namespace OpenRA
} }
public MiniYamlNode(string k, string v, string c = null) public MiniYamlNode(string k, string v, string c = null)
: this(k, v, c, null) { } : this(k, new MiniYaml(v, Enumerable.Empty<MiniYamlNode>()), c) { }
public MiniYamlNode(string k, string v, List<MiniYamlNode> n) public MiniYamlNode(string k, string v, IEnumerable<MiniYamlNode> n)
: this(k, new MiniYaml(v, n), null) { } : this(k, new MiniYaml(v, n), null) { }
public MiniYamlNode(string k, string v, string c, List<MiniYamlNode> n)
: this(k, new MiniYaml(v, n), c) { }
public MiniYamlNode(string k, string v, string c, List<MiniYamlNode> n, SourceLocation loc)
: this(k, new MiniYaml(v, n), c, loc) { }
public override string ToString() public override string ToString()
{ {
return $"{{YamlNode: {Key} @ {Location}}}"; return $"{{YamlNode: {Key} @ {Location}}}";
} }
public MiniYamlNode Clone()
{
return new MiniYamlNode(Key, Value.Clone(), Comment, Location);
}
} }
public sealed class MiniYaml public sealed class MiniYaml
@@ -101,15 +115,30 @@ namespace OpenRA
const int SpacesPerLevel = 4; const int SpacesPerLevel = 4;
static readonly Func<string, string> StringIdentity = s => s; static readonly Func<string, string> StringIdentity = s => s;
static readonly Func<MiniYaml, MiniYaml> MiniYamlIdentity = my => my; static readonly Func<MiniYaml, MiniYaml> MiniYamlIdentity = my => my;
public string Value;
public List<MiniYamlNode> Nodes;
public MiniYaml Clone() public readonly string Value;
public readonly ImmutableArray<MiniYamlNode> Nodes;
public MiniYaml WithValue(string value)
{ {
var clonedNodes = new List<MiniYamlNode>(Nodes.Count); if (Value == value)
foreach (var node in Nodes) return this;
clonedNodes.Add(node.Clone()); return new MiniYaml(value, Nodes);
return new MiniYaml(Value, clonedNodes); }
public MiniYaml WithNodes(IEnumerable<MiniYamlNode> nodes)
{
if (nodes is ImmutableArray<MiniYamlNode> n && Nodes == n)
return this;
return new MiniYaml(Value, nodes);
}
public MiniYaml WithNodesAppended(IEnumerable<MiniYamlNode> nodes)
{
var newNodes = Nodes.AddRange(nodes);
if (Nodes == newNodes)
return this;
return new MiniYaml(Value, newNodes);
} }
public Dictionary<string, MiniYaml> ToDictionary() public Dictionary<string, MiniYaml> ToDictionary()
@@ -125,7 +154,7 @@ namespace OpenRA
public Dictionary<TKey, TElement> ToDictionary<TKey, TElement>( public Dictionary<TKey, TElement> ToDictionary<TKey, TElement>(
Func<string, TKey> keySelector, Func<MiniYaml, TElement> elementSelector) Func<string, TKey> keySelector, Func<MiniYaml, TElement> elementSelector)
{ {
var ret = new Dictionary<TKey, TElement>(Nodes.Count); var ret = new Dictionary<TKey, TElement>(Nodes.Length);
foreach (var y in Nodes) foreach (var y in Nodes)
{ {
var key = keySelector(y.Key); var key = keySelector(y.Key);
@@ -138,28 +167,28 @@ namespace OpenRA
} }
public MiniYaml(string value) public MiniYaml(string value)
: this(value, null) { } : this(value, Enumerable.Empty<MiniYamlNode>()) { }
public MiniYaml(string value, List<MiniYamlNode> nodes) public MiniYaml(string value, IEnumerable<MiniYamlNode> nodes)
{ {
Value = value; Value = value;
Nodes = nodes ?? new List<MiniYamlNode>(); Nodes = ImmutableArray.CreateRange(nodes);
} }
public static List<MiniYamlNode> NodesOrEmpty(MiniYaml y, string s) public static ImmutableArray<MiniYamlNode> NodesOrEmpty(MiniYaml y, string s)
{ {
var nd = y.ToDictionary(); return y.Nodes.FirstOrDefault(n => n.Key == s)?.Value.Nodes ?? ImmutableArray<MiniYamlNode>.Empty;
return nd.TryGetValue(s, out var v) ? v.Nodes : new List<MiniYamlNode>();
} }
static List<MiniYamlNode> FromLines(IEnumerable<ReadOnlyMemory<char>> lines, string filename, bool discardCommentsAndWhitespace, Dictionary<string, string> stringPool) static List<MiniYamlNode> FromLines(IEnumerable<ReadOnlyMemory<char>> lines, string filename, bool discardCommentsAndWhitespace, Dictionary<string, string> stringPool)
{ {
stringPool ??= new Dictionary<string, string>(); stringPool ??= new Dictionary<string, string>();
var levels = new List<List<MiniYamlNode>> var result = new List<List<MiniYamlNode>>
{ {
new List<MiniYamlNode>() new List<MiniYamlNode>()
}; };
var parsedLines = new List<(int Level, string Key, string Value, string Comment, MiniYamlNode.SourceLocation Location)>();
var lineNo = 0; var lineNo = 0;
foreach (var ll in lines) foreach (var ll in lines)
@@ -206,15 +235,9 @@ namespace OpenRA
} }
} }
if (levels.Count <= level) if (parsedLines.Count > 0 && parsedLines[^1].Level < level - 1)
throw new YamlException($"Bad indent in miniyaml at {location}"); throw new YamlException($"Bad indent in miniyaml at {location}");
while (levels.Count > level + 1)
{
levels[^1].TrimExcess();
levels.RemoveAt(levels.Count - 1);
}
// Extract key, value, comment from line as `<key>: <value>#<comment>` // Extract key, value, comment from line as `<key>: <value>#<comment>`
// The # character is allowed in the value if escaped (\#). // The # character is allowed in the value if escaped (\#).
// Leading and trailing whitespace is always trimmed from keys. // Leading and trailing whitespace is always trimmed from keys.
@@ -274,6 +297,9 @@ namespace OpenRA
if (!key.IsEmpty || !discardCommentsAndWhitespace) if (!key.IsEmpty || !discardCommentsAndWhitespace)
{ {
while (parsedLines.Count > 0 && parsedLines[^1].Level > level)
BuildCompletedSubNode(level);
var keyString = key.IsEmpty ? null : key.ToString(); var keyString = key.IsEmpty ? null : key.ToString();
var valueString = value.IsEmpty ? null : value.ToString(); var valueString = value.IsEmpty ? null : value.ToString();
@@ -285,17 +311,46 @@ namespace OpenRA
valueString = valueString == null ? null : stringPool.GetOrAdd(valueString, valueString); valueString = valueString == null ? null : stringPool.GetOrAdd(valueString, valueString);
commentString = commentString == null ? null : stringPool.GetOrAdd(commentString, commentString); commentString = commentString == null ? null : stringPool.GetOrAdd(commentString, commentString);
var nodes = new List<MiniYamlNode>(); parsedLines.Add((level, keyString, valueString, commentString, location));
levels[level].Add(new MiniYamlNode(keyString, valueString, commentString, nodes, location));
levels.Add(nodes);
} }
} }
foreach (var nodes in levels) if (parsedLines.Count > 0)
nodes.TrimExcess(); BuildCompletedSubNode(0);
return levels[0]; return result[0];
void BuildCompletedSubNode(int level)
{
var lastLevel = parsedLines[^1].Level;
while (lastLevel >= result.Count)
result.Add(new List<MiniYamlNode>());
while (parsedLines.Count > 0 && parsedLines[^1].Level >= level)
{
var parent = parsedLines[^1];
var startOfRange = parsedLines.Count - 1;
while (startOfRange > 0 && parsedLines[startOfRange - 1].Level == parent.Level)
startOfRange--;
for (var i = startOfRange; i < parsedLines.Count - 1; i++)
{
var sibling = parsedLines[i];
result[parent.Level].Add(
new MiniYamlNode(sibling.Key, new MiniYaml(sibling.Value), sibling.Comment, sibling.Location));
}
var childNodes = parent.Level + 1 < result.Count ? result[parent.Level + 1] : null;
result[parent.Level].Add(new MiniYamlNode(
parent.Key,
new MiniYaml(parent.Value, childNodes ?? Enumerable.Empty<MiniYamlNode>()),
parent.Comment,
parent.Location));
childNodes?.Clear();
parsedLines.RemoveRange(startOfRange, parsedLines.Count - startOfRange);
}
}
} }
public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null) public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
@@ -313,7 +368,7 @@ namespace OpenRA
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None).Select(s => s.AsMemory()), fileName, discardCommentsAndWhitespace, stringPool); return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None).Select(s => s.AsMemory()), fileName, discardCommentsAndWhitespace, stringPool);
} }
public static List<MiniYamlNode> Merge(IEnumerable<List<MiniYamlNode>> sources) public static List<MiniYamlNode> Merge(IEnumerable<IReadOnlyCollection<MiniYamlNode>> sources)
{ {
var sourcesList = sources.ToList(); var sourcesList = sources.ToList();
if (sourcesList.Count == 0) if (sourcesList.Count == 0)
@@ -336,7 +391,7 @@ namespace OpenRA
} }
// Resolve any top-level removals (e.g. removing whole actor blocks) // Resolve any top-level removals (e.g. removing whole actor blocks)
var nodes = new MiniYaml("", resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList()); var nodes = new MiniYaml("", resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)));
return ResolveInherits(nodes, tree, ImmutableDictionary<string, MiniYamlNode.SourceLocation>.Empty); return ResolveInherits(nodes, tree, ImmutableDictionary<string, MiniYamlNode.SourceLocation>.Empty);
} }
@@ -345,19 +400,23 @@ namespace OpenRA
{ {
if (existingNodeKeys.Add(overrideNode.Key)) if (existingNodeKeys.Add(overrideNode.Key))
{ {
existingNodes.Add(overrideNode.Clone()); existingNodes.Add(overrideNode);
return; return;
} }
var existingNode = existingNodes.Find(n => n.Key == overrideNode.Key); var existingNodeIndex = IndexOfKey(existingNodes, overrideNode.Key);
existingNode.Value = MergePartial(existingNode.Value, overrideNode.Value); var existingNode = existingNodes[existingNodeIndex];
existingNode.Value.Nodes = ResolveInherits(existingNode.Value, tree, inherited); var value = MergePartial(existingNode.Value, overrideNode.Value);
var nodes = ResolveInherits(value, tree, inherited);
if (!value.Nodes.SequenceEqual(nodes))
value = value.WithNodes(nodes);
existingNodes[existingNodeIndex] = existingNode.WithValue(value);
} }
static List<MiniYamlNode> ResolveInherits(MiniYaml node, Dictionary<string, MiniYaml> tree, ImmutableDictionary<string, MiniYamlNode.SourceLocation> inherited) static List<MiniYamlNode> ResolveInherits(MiniYaml node, Dictionary<string, MiniYaml> tree, ImmutableDictionary<string, MiniYamlNode.SourceLocation> inherited)
{ {
var resolved = new List<MiniYamlNode>(node.Nodes.Count); var resolved = new List<MiniYamlNode>(node.Nodes.Length);
var resolvedKeys = new HashSet<string>(node.Nodes.Count); var resolvedKeys = new HashSet<string>(node.Nodes.Length);
foreach (var n in node.Nodes) foreach (var n in node.Nodes)
{ {
@@ -390,7 +449,6 @@ namespace OpenRA
MergeIntoResolved(n, resolved, resolvedKeys, tree, inherited); MergeIntoResolved(n, resolved, resolvedKeys, tree, inherited);
} }
resolved.TrimExcess();
return resolved; return resolved;
} }
@@ -398,7 +456,7 @@ namespace OpenRA
/// Merges any duplicate keys that are defined within the same set of nodes. /// Merges any duplicate keys that are defined within the same set of nodes.
/// Does not resolve inheritance or node removals. /// Does not resolve inheritance or node removals.
/// </summary> /// </summary>
static List<MiniYamlNode> MergeSelfPartial(List<MiniYamlNode> existingNodes) static IReadOnlyCollection<MiniYamlNode> MergeSelfPartial(IReadOnlyCollection<MiniYamlNode> existingNodes)
{ {
var keys = new HashSet<string>(existingNodes.Count); var keys = new HashSet<string>(existingNodes.Count);
var ret = new List<MiniYamlNode>(existingNodes.Count); var ret = new List<MiniYamlNode>(existingNodes.Count);
@@ -409,12 +467,12 @@ namespace OpenRA
else else
{ {
// Node with the same key has already been added: merge new node over the existing one // Node with the same key has already been added: merge new node over the existing one
var original = ret.First(r => r.Key == n.Key); var originalIndex = IndexOfKey(ret, n.Key);
original.Value = MergePartial(original.Value, n.Value); var original = ret[originalIndex];
ret[originalIndex] = original.WithValue(MergePartial(original.Value, n.Value));
} }
} }
ret.TrimExcess();
return ret; return ret;
} }
@@ -432,7 +490,7 @@ namespace OpenRA
return new MiniYaml(overrideNodes.Value ?? existingNodes.Value, MergePartial(existingNodes.Nodes, overrideNodes.Nodes)); return new MiniYaml(overrideNodes.Value ?? existingNodes.Value, MergePartial(existingNodes.Nodes, overrideNodes.Nodes));
} }
static List<MiniYamlNode> MergePartial(List<MiniYamlNode> existingNodes, List<MiniYamlNode> overrideNodes) static IReadOnlyCollection<MiniYamlNode> MergePartial(IReadOnlyCollection<MiniYamlNode> existingNodes, IReadOnlyCollection<MiniYamlNode> overrideNodes)
{ {
if (existingNodes.Count == 0) if (existingNodes.Count == 0)
return overrideNodes; return overrideNodes;
@@ -468,9 +526,8 @@ namespace OpenRA
// A Removal node is closer than the previous node. // A Removal node is closer than the previous node.
// We should not merge the new node, as the data being merged will jump before the Removal. // We should not merge the new node, as the data being merged will jump before the Removal.
// Instead, append it so the previous node is applied, then removed, then the new node is applied. // Instead, append it so the previous node is applied, then removed, then the new node is applied.
var removalKey = $"-{node.Key}"; var previousNodeIndex = LastIndexOfKey(ret, node.Key);
var previousNodeIndex = ret.FindLastIndex(n => n.Key == node.Key); var previousRemovalNodeIndex = LastIndexOfKey(ret, $"-{node.Key}");
var previousRemovalNodeIndex = ret.FindLastIndex(n => n.Key == removalKey);
if (previousRemovalNodeIndex != -1 && previousRemovalNodeIndex > previousNodeIndex) if (previousRemovalNodeIndex != -1 && previousRemovalNodeIndex > previousNodeIndex)
{ {
ret.Add(node); ret.Add(node);
@@ -479,13 +536,30 @@ namespace OpenRA
// A previous node is present with no intervening Removal. // A previous node is present with no intervening Removal.
// We should merge the new one into it, in place. // We should merge the new one into it, in place.
ret[previousNodeIndex] = new MiniYamlNode(node.Key, MergePartial(ret[previousNodeIndex].Value, node.Value), node.Comment, node.Location); ret[previousNodeIndex] = node.WithValue(MergePartial(ret[previousNodeIndex].Value, node.Value));
} }
ret.TrimExcess();
return ret; return ret;
} }
static int IndexOfKey(List<MiniYamlNode> nodes, string key)
{
// PERF: Avoid LINQ.
for (var i = 0; i < nodes.Count; i++)
if (nodes[i].Key == key)
return i;
return -1;
}
static int LastIndexOfKey(List<MiniYamlNode> nodes, string key)
{
// PERF: Avoid LINQ.
for (var i = nodes.Count - 1; i >= 0; i--)
if (nodes[i].Key == key)
return i;
return -1;
}
public IEnumerable<string> ToLines(string key, string comment = null) public IEnumerable<string> ToLines(string key, string comment = null)
{ {
var hasKey = !string.IsNullOrEmpty(key); var hasKey = !string.IsNullOrEmpty(key);
@@ -508,14 +582,94 @@ namespace OpenRA
files = files.Append(mapFiles); files = files.Append(mapFiles);
} }
var yaml = files.Select(s => FromStream(fileSystem.Open(s), s)); IEnumerable<IReadOnlyCollection<MiniYamlNode>> yaml = files.Select(s => FromStream(fileSystem.Open(s), s));
if (mapRules != null && mapRules.Nodes.Count > 0) if (mapRules != null && mapRules.Nodes.Length > 0)
yaml = yaml.Append(mapRules.Nodes); yaml = yaml.Append(mapRules.Nodes);
return Merge(yaml); return Merge(yaml);
} }
} }
public sealed class MiniYamlNodeBuilder
{
public MiniYamlNode.SourceLocation Location;
public string Key;
public MiniYamlBuilder Value;
public string Comment;
public MiniYamlNodeBuilder(MiniYamlNode node)
{
Location = node.Location;
Key = node.Key;
Value = new MiniYamlBuilder(node.Value);
Comment = node.Comment;
}
public MiniYamlNodeBuilder(string k, MiniYamlBuilder v, string c = null)
{
Key = k;
Value = v;
Comment = c;
}
public MiniYamlNodeBuilder(string k, MiniYamlBuilder v, string c, MiniYamlNode.SourceLocation loc)
: this(k, v, c)
{
Location = loc;
}
public MiniYamlNodeBuilder(string k, string v, string c = null)
: this(k, new MiniYamlBuilder(v, null), c) { }
public MiniYamlNodeBuilder(string k, string v, List<MiniYamlNode> n)
: this(k, new MiniYamlBuilder(v, n), null) { }
public MiniYamlNode Build()
{
return new MiniYamlNode(Key, Value.Build(), Comment, Location);
}
}
public sealed class MiniYamlBuilder
{
public string Value;
public List<MiniYamlNodeBuilder> Nodes;
public MiniYamlBuilder(MiniYaml yaml)
{
Value = yaml.Value;
Nodes = yaml.Nodes.Select(n => new MiniYamlNodeBuilder(n)).ToList();
}
public MiniYamlBuilder(string value)
: this(value, null) { }
public MiniYamlBuilder(string value, List<MiniYamlNode> nodes)
{
Value = value;
Nodes = nodes == null ? new List<MiniYamlNodeBuilder>() : nodes.Select(x => new MiniYamlNodeBuilder(x)).ToList();
}
public MiniYaml Build()
{
return new MiniYaml(Value, Nodes.Select(n => n.Build()));
}
public IEnumerable<string> ToLines(string key, string comment = null)
{
var hasKey = !string.IsNullOrEmpty(key);
var hasValue = !string.IsNullOrEmpty(Value);
var hasComment = comment != null;
yield return (hasKey ? key + ":" : "")
+ (hasValue ? " " + Value.Replace("#", "\\#") : "")
+ (hasComment ? (hasKey || hasValue ? " " : "") + "#" + comment : "");
if (Nodes != null)
foreach (var line in Nodes.ToLines())
yield return "\t" + line;
}
}
[Serializable] [Serializable]
public class YamlException : Exception public class YamlException : Exception
{ {

View File

@@ -251,10 +251,8 @@ namespace OpenRA.Network
root.Add(new MiniYamlNode("Mods", Mod + "@" + Version)); root.Add(new MiniYamlNode("Mods", Mod + "@" + Version));
} }
var clientsNode = new MiniYaml(""); var clientsNode = new MiniYaml("", Clients.Select((c, i) =>
var i = 0; new MiniYamlNode("Client@" + i, FieldSaver.Save(c))));
foreach (var c in Clients)
clientsNode.Nodes.Add(new MiniYamlNode("Client@" + i++.ToString(), FieldSaver.Save(c)));
root.Add(new MiniYamlNode("Clients", clientsNode)); root.Add(new MiniYamlNode("Clients", clientsNode));
return new MiniYaml("", root) return new MiniYaml("", root)

View File

@@ -108,10 +108,9 @@ namespace OpenRA.Network
if (arguments != null) if (arguments != null)
{ {
var argumentsNode = new MiniYaml(""); var argumentsNode = new MiniYaml("", arguments
var i = 0; .Select(a => new FluentArgument(a.Key, a.Value))
foreach (var argument in arguments.Select(a => new FluentArgument(a.Key, a.Value))) .Select((argument, i) => new MiniYamlNode("Argument@" + i, FieldSaver.Save(argument))));
argumentsNode.Nodes.Add(new MiniYamlNode("Argument@" + i++, FieldSaver.Save(argument)));
root.Add(new MiniYamlNode("Arguments", argumentsNode)); root.Add(new MiniYamlNode("Arguments", argumentsNode));
} }

View File

@@ -238,8 +238,9 @@ namespace OpenRA.Network
public MiniYamlNode Serialize() public MiniYamlNode Serialize()
{ {
var data = new MiniYamlNode("GlobalSettings", FieldSaver.Save(this)); var data = new MiniYamlNode("GlobalSettings", FieldSaver.Save(this));
var options = LobbyOptions.Select(kv => new MiniYamlNode(kv.Key, FieldSaver.Save(kv.Value))).ToList(); var options = LobbyOptions.Select(kv => new MiniYamlNode(kv.Key, FieldSaver.Save(kv.Value)));
data.Value.Nodes.Add(new MiniYamlNode("Options", new MiniYaml(null, options))); data = data.WithValue(data.Value.WithNodesAppended(
new[] { new MiniYamlNode("Options", new MiniYaml(null, options)) }));
return data; return data;
} }

View File

@@ -361,13 +361,14 @@ namespace OpenRA
public void Save() public void Save()
{ {
var yamlCacheBuilder = yamlCache.Select(n => new MiniYamlNodeBuilder(n)).ToList();
foreach (var kv in Sections) foreach (var kv in Sections)
{ {
var sectionYaml = yamlCache.FirstOrDefault(x => x.Key == kv.Key); var sectionYaml = yamlCacheBuilder.FirstOrDefault(x => x.Key == kv.Key);
if (sectionYaml == null) if (sectionYaml == null)
{ {
sectionYaml = new MiniYamlNode(kv.Key, new MiniYaml("")); sectionYaml = new MiniYamlNodeBuilder(kv.Key, new MiniYamlBuilder(""));
yamlCache.Add(sectionYaml); yamlCacheBuilder.Add(sectionYaml);
} }
var defaultValues = Activator.CreateInstance(kv.Value.GetType()); var defaultValues = Activator.CreateInstance(kv.Value.GetType());
@@ -388,23 +389,25 @@ namespace OpenRA
if (fieldYaml != null) if (fieldYaml != null)
fieldYaml.Value.Value = serialized; fieldYaml.Value.Value = serialized;
else else
sectionYaml.Value.Nodes.Add(new MiniYamlNode(fli.YamlName, new MiniYaml(serialized))); sectionYaml.Value.Nodes.Add(new MiniYamlNodeBuilder(fli.YamlName, new MiniYamlBuilder(serialized)));
} }
} }
} }
var keysYaml = yamlCache.FirstOrDefault(x => x.Key == "Keys"); var keysYaml = yamlCacheBuilder.FirstOrDefault(x => x.Key == "Keys");
if (keysYaml == null) if (keysYaml == null)
{ {
keysYaml = new MiniYamlNode("Keys", new MiniYaml("")); keysYaml = new MiniYamlNodeBuilder("Keys", new MiniYamlBuilder(""));
yamlCache.Add(keysYaml); yamlCacheBuilder.Add(keysYaml);
} }
keysYaml.Value.Nodes.Clear(); keysYaml.Value.Nodes.Clear();
foreach (var kv in Keys) foreach (var kv in Keys)
keysYaml.Value.Nodes.Add(new MiniYamlNode(kv.Key, FieldSaver.FormatValue(kv.Value))); keysYaml.Value.Nodes.Add(new MiniYamlNodeBuilder(kv.Key, FieldSaver.FormatValue(kv.Value)));
yamlCache.WriteToFile(settingsFile); yamlCacheBuilder.WriteToFile(settingsFile);
yamlCache.Clear();
yamlCache.AddRange(yamlCacheBuilder.Select(n => n.Build()));
} }
static string SanitizedName(string dirty) static string SanitizedName(string dirty)

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
@@ -349,7 +350,7 @@ namespace OpenRA.Traits
public interface IGameSaveTraitData public interface IGameSaveTraitData
{ {
List<MiniYamlNode> IssueTraitData(Actor self); List<MiniYamlNode> IssueTraitData(Actor self);
void ResolveTraitData(Actor self, List<MiniYamlNode> data); void ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data);
} }
[RequireExplicitImplementation] [RequireExplicitImplementation]

View File

@@ -128,6 +128,26 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
public abstract void ValidateMapFormat(int format); public abstract void ValidateMapFormat(int format);
protected MiniYamlNodeBuilder GetWorldNodeBuilderFromRules()
{
var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World");
var worldNodeBuilder = worldNode != null
? new MiniYamlNodeBuilder(worldNode)
: new MiniYamlNodeBuilder("World", new MiniYamlBuilder("", new List<MiniYamlNode>()));
return worldNodeBuilder;
}
protected void SaveUpdatedWorldNodeToRules(MiniYamlNodeBuilder worldNodeBuilder)
{
var nodes = Map.RuleDefinitions.Nodes.ToList();
var worldNodeIndex = nodes.FindIndex(n => n.Key == "World");
if (worldNodeIndex != -1)
nodes[worldNodeIndex] = worldNodeBuilder.Build();
else
nodes.Add(worldNodeBuilder.Build());
Map.RuleDefinitions = Map.RuleDefinitions.WithNodes(nodes);
}
void LoadBriefing(IniFile file) void LoadBriefing(IniFile file)
{ {
var briefingSection = file.GetSection("Briefing", true); var briefingSection = file.GetSection("Briefing", true);
@@ -144,21 +164,18 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (briefing.Length == 0) if (briefing.Length == 0)
return; return;
var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World"); var worldNodeBuilder = GetWorldNodeBuilderFromRules();
if (worldNode == null)
{
worldNode = new MiniYamlNode("World", new MiniYaml("", new List<MiniYamlNode>()));
Map.RuleDefinitions.Nodes.Add(worldNode);
}
var missionData = worldNode.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData"); var missionData = worldNodeBuilder.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData");
if (missionData == null) if (missionData == null)
{ {
missionData = new MiniYamlNode("MissionData", new MiniYaml("", new List<MiniYamlNode>())); missionData = new MiniYamlNodeBuilder("MissionData", new MiniYamlBuilder("", new List<MiniYamlNode>()));
worldNode.Value.Nodes.Add(missionData); worldNodeBuilder.Value.Nodes.Add(missionData);
} }
missionData.Value.Nodes.Add(new MiniYamlNode("Briefing", briefing.Replace("\n", " ").ToString())); missionData.Value.Nodes.Add(new MiniYamlNodeBuilder("Briefing", briefing.Replace("\n", " ").ToString()));
SaveUpdatedWorldNodeToRules(worldNodeBuilder);
} }
static void ReplaceInvalidTerrainTiles(Map map) static void ReplaceInvalidTerrainTiles(Map map)
@@ -190,7 +207,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
void LoadVideos(IniFile file, string section) void LoadVideos(IniFile file, string section)
{ {
var videos = new List<MiniYamlNode>(); var videos = new List<MiniYamlNodeBuilder>();
foreach (var s in file.GetSection(section)) foreach (var s in file.GetSection(section))
{ {
if (s.Value != "x" && s.Value != "X" && s.Value != "<none>") if (s.Value != "x" && s.Value != "X" && s.Value != "<none>")
@@ -198,19 +215,19 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
switch (s.Key) switch (s.Key)
{ {
case "Intro": case "Intro":
videos.Add(new MiniYamlNode("BackgroundVideo", s.Value.ToLowerInvariant() + ".vqa")); videos.Add(new MiniYamlNodeBuilder("BackgroundVideo", s.Value.ToLowerInvariant() + ".vqa"));
break; break;
case "Brief": case "Brief":
videos.Add(new MiniYamlNode("BriefingVideo", s.Value.ToLowerInvariant() + ".vqa")); videos.Add(new MiniYamlNodeBuilder("BriefingVideo", s.Value.ToLowerInvariant() + ".vqa"));
break; break;
case "Action": case "Action":
videos.Add(new MiniYamlNode("StartVideo", s.Value.ToLowerInvariant() + ".vqa")); videos.Add(new MiniYamlNodeBuilder("StartVideo", s.Value.ToLowerInvariant() + ".vqa"));
break; break;
case "Win": case "Win":
videos.Add(new MiniYamlNode("WinVideo", s.Value.ToLowerInvariant() + ".vqa")); videos.Add(new MiniYamlNodeBuilder("WinVideo", s.Value.ToLowerInvariant() + ".vqa"));
break; break;
case "Lose": case "Lose":
videos.Add(new MiniYamlNode("LossVideo", s.Value.ToLowerInvariant() + ".vqa")); videos.Add(new MiniYamlNodeBuilder("LossVideo", s.Value.ToLowerInvariant() + ".vqa"));
break; break;
} }
} }
@@ -218,21 +235,18 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (videos.Count > 0) if (videos.Count > 0)
{ {
var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World"); var worldNodeBuilder = GetWorldNodeBuilderFromRules();
if (worldNode == null)
{
worldNode = new MiniYamlNode("World", new MiniYaml("", new List<MiniYamlNode>()));
Map.RuleDefinitions.Nodes.Add(worldNode);
}
var missionData = worldNode.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData"); var missionData = worldNodeBuilder.Value.Nodes.FirstOrDefault(n => n.Key == "MissionData");
if (missionData == null) if (missionData == null)
{ {
missionData = new MiniYamlNode("MissionData", new MiniYaml("", new List<MiniYamlNode>())); missionData = new MiniYamlNodeBuilder("MissionData", new MiniYamlBuilder("", new List<MiniYamlNode>()));
worldNode.Value.Nodes.Add(missionData); worldNodeBuilder.Value.Nodes.Add(missionData);
} }
missionData.Value.Nodes.AddRange(videos); missionData.Value.Nodes.AddRange(videos);
SaveUpdatedWorldNodeToRules(worldNodeBuilder);
} }
} }
@@ -264,13 +278,13 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
void LoadWaypoints(IniSection waypointSection) void LoadWaypoints(IniSection waypointSection)
{ {
var actorCount = Map.ActorDefinitions.Count;
var wps = waypointSection var wps = waypointSection
.Where(kv => Exts.ParseIntegerInvariant(kv.Value) > 0) .Where(kv => Exts.ParseIntegerInvariant(kv.Value) > 0)
.Select(kv => (WaypointNumber: Exts.ParseIntegerInvariant(kv.Key), .Select(kv => (WaypointNumber: Exts.ParseIntegerInvariant(kv.Key),
Location: LocationFromMapOffset(Exts.ParseIntegerInvariant(kv.Value), MapSize))); Location: LocationFromMapOffset(Exts.ParseIntegerInvariant(kv.Value), MapSize)));
// Add waypoint actors skipping duplicate entries // Add waypoint actors skipping duplicate entries
var nodes = new List<MiniYamlNode>();
foreach (var (waypointNumber, location) in wps.DistinctBy(location => location.Location)) foreach (var (waypointNumber, location) in wps.DistinctBy(location => location.Location))
{ {
if (!singlePlayer && waypointNumber <= 7) if (!singlePlayer && waypointNumber <= 7)
@@ -281,7 +295,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
Map.ActorDefinitions.Add(new MiniYamlNode("Actor" + actorCount++, ar.Save())); nodes.Add(new MiniYamlNode("Actor" + (Map.ActorDefinitions.Count + nodes.Count), ar.Save()));
spawnCount++; spawnCount++;
} }
else else
@@ -292,15 +306,17 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
SaveWaypoint(waypointNumber, ar); nodes.Add(SaveWaypoint(waypointNumber, ar));
} }
} }
Map.ActorDefinitions = Map.ActorDefinitions.Concat(nodes).ToArray();
} }
public virtual void SaveWaypoint(int waypointNumber, ActorReference waypointReference) public virtual MiniYamlNode SaveWaypoint(int waypointNumber, ActorReference waypointReference)
{ {
var waypointName = "waypoint" + waypointNumber; var waypointName = "waypoint" + waypointNumber;
Map.ActorDefinitions.Add(new MiniYamlNode(waypointName, waypointReference.Save())); return new MiniYamlNode(waypointName, waypointReference.Save());
} }
void LoadSmudges(IniFile file, string section) void LoadSmudges(IniFile file, string section)
@@ -322,25 +338,24 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
craters.Add(node); craters.Add(node);
} }
var worldNode = Map.RuleDefinitions.Nodes.FirstOrDefault(n => n.Key == "World"); var worldNodeBuilder = GetWorldNodeBuilderFromRules();
worldNode ??= new MiniYamlNode("World", new MiniYaml("", new List<MiniYamlNode>()));
if (scorches.Count > 0) if (scorches.Count > 0)
{ {
var initialScorches = new MiniYamlNode("InitialSmudges", new MiniYaml("", scorches)); var initialScorches = new MiniYamlNode("InitialSmudges", new MiniYaml("", scorches));
var smudgeLayer = new MiniYamlNode("SmudgeLayer@SCORCH", new MiniYaml("", new List<MiniYamlNode>() { initialScorches })); var smudgeLayer = new MiniYamlNodeBuilder("SmudgeLayer@SCORCH", new MiniYamlBuilder("", new List<MiniYamlNode>() { initialScorches }));
worldNode.Value.Nodes.Add(smudgeLayer); worldNodeBuilder.Value.Nodes.Add(smudgeLayer);
} }
if (craters.Count > 0) if (craters.Count > 0)
{ {
var initialCraters = new MiniYamlNode("InitialSmudges", new MiniYaml("", craters)); var initialCraters = new MiniYamlNode("InitialSmudges", new MiniYaml("", craters));
var smudgeLayer = new MiniYamlNode("SmudgeLayer@CRATER", new MiniYaml("", new List<MiniYamlNode>() { initialCraters })); var smudgeLayer = new MiniYamlNodeBuilder("SmudgeLayer@CRATER", new MiniYamlBuilder("", new List<MiniYamlNode>() { initialCraters }));
worldNode.Value.Nodes.Add(smudgeLayer); worldNodeBuilder.Value.Nodes.Add(smudgeLayer);
} }
if (worldNode.Value.Nodes.Count > 0 && !Map.RuleDefinitions.Nodes.Contains(worldNode)) if (worldNodeBuilder.Value.Nodes.Count > 0)
Map.RuleDefinitions.Nodes.Add(worldNode); SaveUpdatedWorldNodeToRules(worldNodeBuilder);
} }
// TODO: fix this -- will have bitrotted pretty badly. // TODO: fix this -- will have bitrotted pretty badly.
@@ -395,6 +410,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
public void LoadActors(IniFile file, string section, List<string> players, Map map) public void LoadActors(IniFile file, string section, List<string> players, Map map)
{ {
var nodes = new List<MiniYamlNode>();
foreach (var s in file.GetSection(section, true)) foreach (var s in file.GetSection(section, true))
{ {
// Structures: num=owner,type,health,location,turret-facing,trigger // Structures: num=owner,type,health,location,turret-facing,trigger
@@ -429,18 +445,18 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (section == "INFANTRY") if (section == "INFANTRY")
actor.Add(new SubCellInit((SubCell)Exts.ParseByte(parts[4]))); actor.Add(new SubCellInit((SubCell)Exts.ParseByte(parts[4])));
var actorCount = map.ActorDefinitions.Count;
if (!map.Rules.Actors.ContainsKey(parts[1].ToLowerInvariant())) if (!map.Rules.Actors.ContainsKey(parts[1].ToLowerInvariant()))
Console.WriteLine($"Ignoring unknown actor type: `{parts[1].ToLowerInvariant()}`"); Console.WriteLine($"Ignoring unknown actor type: `{parts[1].ToLowerInvariant()}`");
else else
map.ActorDefinitions.Add(new MiniYamlNode("Actor" + actorCount++, actor.Save())); nodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + nodes.Count), actor.Save()));
} }
catch (Exception) catch (Exception)
{ {
Console.WriteLine($"Malformed actor definition: `{s}`"); Console.WriteLine($"Malformed actor definition: `{s}`");
} }
} }
map.ActorDefinitions = map.ActorDefinitions.Concat(nodes).ToArray();
} }
public abstract string ParseTreeActor(string input); public abstract string ParseTreeActor(string input);
@@ -451,6 +467,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (terrain == null) if (terrain == null)
return; return;
var nodes = new List<MiniYamlNode>();
foreach (var kv in terrain) foreach (var kv in terrain)
{ {
var loc = Exts.ParseIntegerInvariant(kv.Key); var loc = Exts.ParseIntegerInvariant(kv.Key);
@@ -462,9 +479,10 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
var actorCount = Map.ActorDefinitions.Count; nodes.Add(new MiniYamlNode("Actor" + (Map.ActorDefinitions.Count + nodes.Count), ar.Save()));
Map.ActorDefinitions.Add(new MiniYamlNode("Actor" + actorCount++, ar.Save()));
} }
Map.ActorDefinitions = Map.ActorDefinitions.Concat(nodes).ToArray();
} }
} }

View File

@@ -171,6 +171,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
} }
} }
var nodes = new List<MiniYamlNode>();
foreach (var cell in map.AllCells) foreach (var cell in map.AllCells)
{ {
var overlayType = overlayPack[overlayIndex[cell]]; var overlayType = overlayPack[overlayIndex[cell]];
@@ -180,7 +181,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (TryHandleOverlayToActorInner(cell, overlayPack, overlayIndex, overlayType, out var ar)) if (TryHandleOverlayToActorInner(cell, overlayPack, overlayIndex, overlayType, out var ar))
{ {
if (ar != null) if (ar != null)
map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); nodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + nodes.Count), ar.Save()));
continue; continue;
} }
@@ -196,10 +197,13 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
Console.WriteLine($"Cell {cell}: unknown overlay {overlayType}"); Console.WriteLine($"Cell {cell}: unknown overlay {overlayType}");
} }
map.ActorDefinitions = map.ActorDefinitions.Concat(nodes).ToArray();
} }
protected virtual void ReadWaypoints(Map map, IniFile file, int2 fullSize) protected virtual void ReadWaypoints(Map map, IniFile file, int2 fullSize)
{ {
var nodes = new List<MiniYamlNode>();
var waypointsSection = file.GetSection("Waypoints", true); var waypointsSection = file.GetSection("Waypoints", true);
foreach (var kv in waypointsSection) foreach (var kv in waypointsSection)
{ {
@@ -214,12 +218,15 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); nodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + nodes.Count), ar.Save()));
} }
map.ActorDefinitions = map.ActorDefinitions.Concat(nodes).ToArray();
} }
protected virtual void ReadTerrainActors(Map map, IniFile file, int2 fullSize) protected virtual void ReadTerrainActors(Map map, IniFile file, int2 fullSize)
{ {
var nodes = new List<MiniYamlNode>();
var terrainSection = file.GetSection("Terrain", true); var terrainSection = file.GetSection("Terrain", true);
foreach (var kv in terrainSection) foreach (var kv in terrainSection)
{ {
@@ -238,12 +245,15 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (!map.Rules.Actors.ContainsKey(name)) if (!map.Rules.Actors.ContainsKey(name))
Console.WriteLine($"Ignoring unknown actor type: `{name}`"); Console.WriteLine($"Ignoring unknown actor type: `{name}`");
else else
map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); nodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + nodes.Count), ar.Save()));
} }
map.ActorDefinitions = map.ActorDefinitions.Concat(nodes).ToArray();
} }
protected virtual void ReadActors(Map map, IniFile file, string type, int2 fullSize) protected virtual void ReadActors(Map map, IniFile file, string type, int2 fullSize)
{ {
var nodes = new List<MiniYamlNode>();
var structuresSection = file.GetSection(type, true); var structuresSection = file.GetSection(type, true);
foreach (var kv in structuresSection) foreach (var kv in structuresSection)
{ {
@@ -296,8 +306,10 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (!map.Rules.Actors.ContainsKey(name)) if (!map.Rules.Actors.ContainsKey(name))
Console.WriteLine($"Ignoring unknown actor type: `{name}`"); Console.WriteLine($"Ignoring unknown actor type: `{name}`");
else else
map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); nodes.Add(new MiniYamlNode("Actor" + (map.ActorDefinitions.Count + nodes.Count), ar.Save()));
} }
map.ActorDefinitions = map.ActorDefinitions.Concat(nodes).ToArray();
} }
protected virtual void ReadLighting(Map map, IniFile file) protected virtual void ReadLighting(Map map, IniFile file)
@@ -340,10 +352,13 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (lightingNodes.Count > 0) if (lightingNodes.Count > 0)
{ {
map.RuleDefinitions.Nodes.Add(new MiniYamlNode("^BaseWorld", new MiniYaml("", new List<MiniYamlNode>() map.RuleDefinitions = map.RuleDefinitions.WithNodesAppended(new[]
{ {
new MiniYamlNode("TerrainLighting", new MiniYaml("", lightingNodes)) new MiniYamlNode("^BaseWorld", new MiniYaml("", new[]
}))); {
new MiniYamlNode("TerrainLighting", new MiniYaml("", lightingNodes))
}))
});
} }
} }
@@ -357,6 +372,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
{ "LightBlueTint", "BlueTint" }, { "LightBlueTint", "BlueTint" },
}; };
var nodes = new List<MiniYamlNode>();
foreach (var lamp in LampActors) foreach (var lamp in LampActors)
{ {
var lightingSection = file.GetSection(lamp, true); var lightingSection = file.GetSection(lamp, true);
@@ -380,12 +396,14 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (lightingNodes.Count > 0) if (lightingNodes.Count > 0)
{ {
map.RuleDefinitions.Nodes.Add(new MiniYamlNode(lamp, new MiniYaml("", new List<MiniYamlNode>() nodes.Add(new MiniYamlNode(lamp, new MiniYaml("", new[]
{ {
new MiniYamlNode("TerrainLightSource", new MiniYaml("", lightingNodes)) new MiniYamlNode("TerrainLightSource", new MiniYaml("", lightingNodes))
}))); })));
} }
} }
map.RuleDefinitions = map.RuleDefinitions.WithNodesAppended(nodes);
} }
protected virtual void SetInteractableBounds(Map map, int[] iniBounds) protected virtual void SetInteractableBounds(Map map, int[] iniBounds)

View File

@@ -94,6 +94,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
void UnpackOverlayData(MemoryStream ms) void UnpackOverlayData(MemoryStream ms)
{ {
var nodes = new List<MiniYamlNode>();
for (var j = 0; j < MapSize; j++) for (var j = 0; j < MapSize; j++)
{ {
for (var i = 0; i < MapSize; i++) for (var i = 0; i < MapSize; i++)
@@ -115,11 +116,12 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
var actorCount = Map.ActorDefinitions.Count; nodes.Add(new MiniYamlNode("Actor" + (Map.ActorDefinitions.Count + nodes.Count), ar.Save()));
Map.ActorDefinitions.Add(new MiniYamlNode("Actor" + actorCount++, ar.Save()));
} }
} }
} }
Map.ActorDefinitions = Map.ActorDefinitions.Concat(nodes).ToArray();
} }
public override string ParseTreeActor(string input) public override string ParseTreeActor(string input)
@@ -241,12 +243,12 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
LoadActors(file, "SHIPS", Players, Map); LoadActors(file, "SHIPS", Players, Map);
} }
public override void SaveWaypoint(int waypointNumber, ActorReference waypointReference) public override MiniYamlNode SaveWaypoint(int waypointNumber, ActorReference waypointReference)
{ {
var waypointName = "waypoint" + waypointNumber; var waypointName = "waypoint" + waypointNumber;
if (waypointNumber == 98) if (waypointNumber == 98)
waypointName = "DefaultCameraPosition"; waypointName = "DefaultCameraPosition";
Map.ActorDefinitions.Add(new MiniYamlNode(waypointName, waypointReference.Save())); return new MiniYamlNode(waypointName, waypointReference.Save());
} }
} }
} }

View File

@@ -86,6 +86,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
if (overlay == null) if (overlay == null)
return; return;
var nodes = new List<MiniYamlNode>();
foreach (var kv in overlay) foreach (var kv in overlay)
{ {
var loc = Exts.ParseIntegerInvariant(kv.Key); var loc = Exts.ParseIntegerInvariant(kv.Key);
@@ -105,10 +106,11 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
new OwnerInit("Neutral") new OwnerInit("Neutral")
}; };
var actorCount = Map.ActorDefinitions.Count; nodes.Add(new MiniYamlNode("Actor" + (Map.ActorDefinitions.Count + nodes.Count), ar.Save()));
Map.ActorDefinitions.Add(new MiniYamlNode("Actor" + actorCount++, ar.Save()));
} }
} }
Map.ActorDefinitions = Map.ActorDefinitions.Concat(nodes).ToArray();
} }
public override string ParseTreeActor(string input) public override string ParseTreeActor(string input)
@@ -170,7 +172,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
ReadOverlay(file); ReadOverlay(file);
} }
public override void SaveWaypoint(int waypointNumber, ActorReference waypointReference) public override MiniYamlNode SaveWaypoint(int waypointNumber, ActorReference waypointReference)
{ {
var waypointName = "waypoint" + waypointNumber; var waypointName = "waypoint" + waypointNumber;
if (waypointNumber == 25) if (waypointNumber == 25)
@@ -179,7 +181,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
waypointName = "DefaultCameraPosition"; waypointName = "DefaultCameraPosition";
else if (waypointNumber == 27) else if (waypointNumber == 27)
waypointName = "DefaultChinookTarget"; waypointName = "DefaultChinookTarget";
Map.ActorDefinitions.Add(new MiniYamlNode(waypointName, waypointReference.Save())); return new MiniYamlNode(waypointName, waypointReference.Save());
} }
} }
} }

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Graphics
var sequences = new Dictionary<string, ISpriteSequence>(); var sequences = new Dictionary<string, ISpriteSequence>();
var node = imageNode.Value.Nodes.SingleOrDefault(n => n.Key == "Defaults"); var node = imageNode.Value.Nodes.SingleOrDefault(n => n.Key == "Defaults");
var defaults = node?.Value ?? NoData; var defaults = node?.Value ?? NoData;
imageNode.Value.Nodes.Remove(node); imageNode = imageNode.WithValue(imageNode.Value.WithNodes(imageNode.Value.Nodes.Remove(node)));
foreach (var sequenceNode in imageNode.Value.Nodes) foreach (var sequenceNode in imageNode.Value.Nodes)
{ {
@@ -262,7 +262,7 @@ namespace OpenRA.Mods.Common.Graphics
protected static T LoadField<T>(string key, T fallback, MiniYaml data, MiniYaml defaults = null) protected static T LoadField<T>(string key, T fallback, MiniYaml data, MiniYaml defaults = null)
{ {
var node = data.Nodes.Find(n => n.Key == key) ?? defaults?.Nodes.Find(n => n.Key == key); var node = data.Nodes.FirstOrDefault(n => n.Key == key) ?? defaults?.Nodes.FirstOrDefault(n => n.Key == key);
if (node == null) if (node == null)
return fallback; return fallback;
@@ -276,7 +276,7 @@ namespace OpenRA.Mods.Common.Graphics
protected static T LoadField<T>(SpriteSequenceField<T> field, MiniYaml data, MiniYaml defaults, out MiniYamlNode.SourceLocation location) protected static T LoadField<T>(SpriteSequenceField<T> field, MiniYaml data, MiniYaml defaults, out MiniYamlNode.SourceLocation location)
{ {
var node = data.Nodes.Find(n => n.Key == field.Key) ?? defaults?.Nodes.Find(n => n.Key == field.Key); var node = data.Nodes.FirstOrDefault(n => n.Key == field.Key) ?? defaults?.Nodes.FirstOrDefault(n => n.Key == field.Key);
if (node == null) if (node == null)
{ {
location = default; location = default;
@@ -414,10 +414,10 @@ namespace OpenRA.Mods.Common.Graphics
var offset = LoadField(Offset, data, defaults); var offset = LoadField(Offset, data, defaults);
var blendMode = LoadField(BlendMode, data, defaults); var blendMode = LoadField(BlendMode, data, defaults);
var combineNode = data.Nodes.Find(n => n.Key == Combine.Key); var combineNode = data.Nodes.FirstOrDefault(n => n.Key == Combine.Key);
if (combineNode != null) if (combineNode != null)
{ {
for (var i = 0; i < combineNode.Value.Nodes.Count; i++) for (var i = 0; i < combineNode.Value.Nodes.Length; i++)
{ {
var subData = combineNode.Value.Nodes[i].Value; var subData = combineNode.Value.Nodes[i].Value;
var subOffset = LoadField(Offset, subData, NoData); var subOffset = LoadField(Offset, subData, NoData);

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Widgets; using OpenRA.Widgets;
@@ -64,7 +65,7 @@ namespace OpenRA.Mods.Common.Lint
} }
void CheckInner(ModData modData, string[] namedKeys, (string Widget, string Field)[] checkWidgetFields, Dictionary<string, List<string>> customLintMethods, void CheckInner(ModData modData, string[] namedKeys, (string Widget, string Field)[] checkWidgetFields, Dictionary<string, List<string>> customLintMethods,
List<MiniYamlNode> nodes, string filename, MiniYamlNode parent, Action<string> emitError) IEnumerable<MiniYamlNode> nodes, string filename, MiniYamlNode parent, Action<string> emitError)
{ {
foreach (var node in nodes) foreach (var node in nodes)
{ {
@@ -94,7 +95,7 @@ namespace OpenRA.Mods.Common.Lint
} }
// Logic classes can declare the data key names that specify hotkeys. // Logic classes can declare the data key names that specify hotkeys.
if (node.Key == "Logic" && node.Value.Nodes.Count > 0) if (node.Key == "Logic" && node.Value.Nodes.Length > 0)
{ {
var typeNames = FieldLoader.GetValue<string[]>(node.Key, node.Value.Value); var typeNames = FieldLoader.GetValue<string[]>(node.Key, node.Value.Value);
var checkArgKeys = new List<string>(); var checkArgKeys = new List<string>();

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.Lint
CheckInner(MiniYaml.FromStream(modData.DefaultFileSystem.Open(filename), filename), filename, emitError); CheckInner(MiniYaml.FromStream(modData.DefaultFileSystem.Open(filename), filename), filename, emitError);
} }
void CheckInner(List<MiniYamlNode> nodes, string filename, Action<string> emitError) void CheckInner(IEnumerable<MiniYamlNode> nodes, string filename, Action<string> emitError)
{ {
var substitutions = new Dictionary<string, int>(); var substitutions = new Dictionary<string, int>();
var readOnlySubstitutions = new ReadOnlyDictionary<string, int>(substitutions); var readOnlySubstitutions = new ReadOnlyDictionary<string, int>(substitutions);

View File

@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Lint
CheckInner(MiniYaml.FromStream(modData.DefaultFileSystem.Open(filename), filename), filename, emitError); CheckInner(MiniYaml.FromStream(modData.DefaultFileSystem.Open(filename), filename), filename, emitError);
} }
void CheckInner(List<MiniYamlNode> nodes, string filename, Action<string> emitError) void CheckInner(IEnumerable<MiniYamlNode> nodes, string filename, Action<string> emitError)
{ {
foreach (var node in nodes) foreach (var node in nodes)
{ {

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Lint
// Removals can never define children or values. // Removals can never define children or values.
if (t.Key.StartsWith("-", StringComparison.Ordinal)) if (t.Key.StartsWith("-", StringComparison.Ordinal))
{ {
if (t.Value.Nodes.Count > 0) if (t.Value.Nodes.Length > 0)
emitError($"{t.Location} `{t.Key}` defines child nodes, which are not valid for removals."); emitError($"{t.Location} `{t.Key}` defines child nodes, which are not valid for removals.");
if (!string.IsNullOrEmpty(t.Value.Value)) if (!string.IsNullOrEmpty(t.Value.Value))
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Lint
// Inherits can never define children. // Inherits can never define children.
if (traitName == "Inherits") if (traitName == "Inherits")
{ {
if (t.Value.Nodes.Count > 0) if (t.Value.Nodes.Length > 0)
emitError($"{t.Location} defines child nodes, which are not valid for Inherits."); emitError($"{t.Location} defines child nodes, which are not valid for Inherits.");
continue; continue;
@@ -98,7 +98,7 @@ namespace OpenRA.Mods.Common.Lint
foreach (var f in mapFiles) foreach (var f in mapFiles)
CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData); CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData);
if (ruleDefinitions.Nodes.Count > 0) if (ruleDefinitions.Nodes.Length > 0)
CheckActors(ruleDefinitions.Nodes, emitError, modData); CheckActors(ruleDefinitions.Nodes, emitError, modData);
} }
} }

View File

@@ -54,7 +54,7 @@ namespace OpenRA.Mods.Common.Lint
// Removals can never define children or values // Removals can never define children or values
if (field.Key.StartsWith("-", StringComparison.Ordinal)) if (field.Key.StartsWith("-", StringComparison.Ordinal))
{ {
if (field.Value.Nodes.Count > 0) if (field.Value.Nodes.Length > 0)
emitError($"{field.Location} `{field.Key}` defines child nodes, which is not valid for removals."); emitError($"{field.Location} `{field.Key}` defines child nodes, which is not valid for removals.");
if (!string.IsNullOrEmpty(field.Value.Value)) if (!string.IsNullOrEmpty(field.Value.Value))
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Lint
foreach (var f in mapFiles) foreach (var f in mapFiles)
CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData); CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData);
if (weaponDefinitions.Nodes.Count > 0) if (weaponDefinitions.Nodes.Length > 0)
CheckWeapons(weaponDefinitions.Nodes, emitError, emitWarning, modData); CheckWeapons(weaponDefinitions.Nodes, emitError, emitWarning, modData);
} }
} }

View File

@@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Lint
foreach (var actorNode in nodes) foreach (var actorNode in nodes)
{ {
var inherits = inheritsMap.GetOrAdd(actorNode.Key, _ => new List<string>()); var inherits = inheritsMap.GetOrAdd(actorNode.Key, _ => new List<string>());
foreach (var inheritsNode in actorNode.ChildrenMatching("Inherits")) foreach (var inheritsNode in new MiniYamlNodeBuilder(actorNode).ChildrenMatching("Inherits"))
inherits.Add(inheritsNode.Value.Value); inherits.Add(inheritsNode.Value.Value);
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -56,7 +57,7 @@ namespace OpenRA
public readonly MiniYaml IDFiles; public readonly MiniYaml IDFiles;
[FieldLoader.Ignore] [FieldLoader.Ignore]
public readonly List<MiniYamlNode> Install; public readonly ImmutableArray<MiniYamlNode> Install;
public readonly string TooltipText; public readonly string TooltipText;

View File

@@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Terrain
} }
else else
{ {
tileInfo = new TerrainTileInfo[nodes.Count]; tileInfo = new TerrainTileInfo[nodes.Length];
var i = 0; var i = 0;
foreach (var node in nodes) foreach (var node in nodes)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -275,7 +276,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
if (self.World.IsReplay) if (self.World.IsReplay)
return; return;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -211,7 +212,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
if (self.World.IsReplay) if (self.World.IsReplay)
return; return;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits.BotModules.Squads; using OpenRA.Mods.Common.Traits.BotModules.Squads;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -407,7 +408,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
if (self.World.IsReplay) if (self.World.IsReplay)
return; return;

View File

@@ -84,16 +84,15 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
public MiniYaml Serialize() public MiniYaml Serialize()
{ {
var nodes = new MiniYaml("", new List<MiniYamlNode>() var nodes = new List<MiniYamlNode>()
{ {
new MiniYamlNode("Type", FieldSaver.FormatValue(Type)), new MiniYamlNode("Type", FieldSaver.FormatValue(Type)),
new MiniYamlNode("Units", FieldSaver.FormatValue(Units.Select(a => a.ActorID).ToArray())), new MiniYamlNode("Units", FieldSaver.FormatValue(Units.Select(a => a.ActorID).ToArray())),
}); };
if (Target.Type == TargetType.Actor) if (Target.Type == TargetType.Actor)
nodes.Nodes.Add(new MiniYamlNode("Target", FieldSaver.FormatValue(Target.Actor.ActorID))); nodes.Add(new MiniYamlNode("Target", FieldSaver.FormatValue(Target.Actor.ActorID)));
return nodes; return new MiniYaml("", nodes);
} }
public static Squad Deserialize(IBot bot, SquadManagerBotModule squadManager, MiniYaml yaml) public static Squad Deserialize(IBot bot, SquadManagerBotModule squadManager, MiniYaml yaml)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -222,7 +223,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
if (self.World.IsReplay) if (self.World.IsReplay)
return; return;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -221,7 +222,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
if (self.World.IsReplay) if (self.World.IsReplay)
return; return;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Traits; using OpenRA.Traits;
@@ -49,7 +50,7 @@ namespace OpenRA.Mods.Common.Traits
return nodes; return nodes;
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
var viewportNode = data.FirstOrDefault(n => n.Key == "Viewport"); var viewportNode = data.FirstOrDefault(n => n.Key == "Viewport");
if (viewportNode != null) if (viewportNode != null)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -132,7 +133,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
var groupsNode = data.FirstOrDefault(n => n.Key == "Groups"); var groupsNode = data.FirstOrDefault(n => n.Key == "Groups");
if (groupsNode != null) if (groupsNode != null)

View File

@@ -85,7 +85,7 @@ namespace OpenRA.Mods.Common.Traits
protected static object LoadSpeeds(MiniYaml y) protected static object LoadSpeeds(MiniYaml y)
{ {
var speeds = y.ToDictionary()["TerrainSpeeds"].Nodes; var speeds = y.ToDictionary()["TerrainSpeeds"].Nodes;
var ret = new Dictionary<string, TerrainInfo>(speeds.Count); var ret = new Dictionary<string, TerrainInfo>(speeds.Length);
foreach (var t in speeds) foreach (var t in speeds)
{ {
var speed = FieldLoader.GetValue<int>("speed", t.Value.Value); var speed = FieldLoader.GetValue<int>("speed", t.Value.Value);

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -182,7 +183,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
} }
void IGameSaveTraitData.ResolveTraitData(Actor self, List<MiniYamlNode> data) void IGameSaveTraitData.ResolveTraitData(Actor self, ImmutableArray<MiniYamlNode> data)
{ {
var selectionNode = data.FirstOrDefault(n => n.Key == "Selection"); var selectionNode = data.FirstOrDefault(n => n.Key == "Selection");
if (selectionNode != null) if (selectionNode != null)

View File

@@ -72,9 +72,9 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
harvesterPipLocations.Clear(); harvesterPipLocations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var addNodes = new List<MiniYamlNode>(); var addNodes = new List<MiniYamlNodeBuilder>();
foreach (var selectionDecorations in actorNode.ChildrenMatching("SelectionDecorations")) foreach (var selectionDecorations in actorNode.ChildrenMatching("SelectionDecorations"))
{ {
@@ -84,7 +84,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
foreach (var ammoPool in actorNode.ChildrenMatching("AmmoPool")) foreach (var ammoPool in actorNode.ChildrenMatching("AmmoPool"))
{ {
var ammoPips = new MiniYamlNode("WithAmmoPipsDecoration", ""); var ammoPips = new MiniYamlNodeBuilder("WithAmmoPipsDecoration", "");
ammoPips.AddNode("Position", "BottomLeft"); ammoPips.AddNode("Position", "BottomLeft");
ammoPips.AddNode("RequiresSelection", "true"); ammoPips.AddNode("RequiresSelection", "true");
@@ -95,7 +95,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var pipCount = pipCountNode.NodeValue<int>(); var pipCount = pipCountNode.NodeValue<int>();
if (pipCount == 0) if (pipCount == 0)
{ {
addNodes.Add(new MiniYamlNode("-" + ammoPips.Key, "")); addNodes.Add(new MiniYamlNodeBuilder("-" + ammoPips.Key, ""));
continue; continue;
} }
@@ -129,7 +129,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
foreach (var cargo in actorNode.ChildrenMatching("Cargo")) foreach (var cargo in actorNode.ChildrenMatching("Cargo"))
{ {
var cargoPips = new MiniYamlNode("WithCargoPipsDecoration", ""); var cargoPips = new MiniYamlNodeBuilder("WithCargoPipsDecoration", "");
cargoPips.AddNode("Position", "BottomLeft"); cargoPips.AddNode("Position", "BottomLeft");
cargoPips.AddNode("RequiresSelection", "true"); cargoPips.AddNode("RequiresSelection", "true");
@@ -141,7 +141,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var pipCount = pipCountNode.NodeValue<int>(); var pipCount = pipCountNode.NodeValue<int>();
if (pipCount == 0) if (pipCount == 0)
{ {
addNodes.Add(new MiniYamlNode("-" + cargoPips.Key, "")); addNodes.Add(new MiniYamlNodeBuilder("-" + cargoPips.Key, ""));
continue; continue;
} }
@@ -171,7 +171,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
foreach (var harvester in actorNode.ChildrenMatching("Harvester")) foreach (var harvester in actorNode.ChildrenMatching("Harvester"))
{ {
var harvesterPips = new MiniYamlNode("WithHarvesterPipsDecoration", ""); var harvesterPips = new MiniYamlNodeBuilder("WithHarvesterPipsDecoration", "");
harvesterPips.AddNode("Position", "BottomLeft"); harvesterPips.AddNode("Position", "BottomLeft");
harvesterPips.AddNode("RequiresSelection", "true"); harvesterPips.AddNode("RequiresSelection", "true");
@@ -189,7 +189,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var pipCount = pipCountNode.NodeValue<int>(); var pipCount = pipCountNode.NodeValue<int>();
if (pipCount == 0) if (pipCount == 0)
{ {
addNodes.Add(new MiniYamlNode("-" + harvesterPips.Key, "")); addNodes.Add(new MiniYamlNodeBuilder("-" + harvesterPips.Key, ""));
continue; continue;
} }
@@ -220,7 +220,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
foreach (var storesResources in actorNode.ChildrenMatching("StoresResources")) foreach (var storesResources in actorNode.ChildrenMatching("StoresResources"))
{ {
var storagePips = new MiniYamlNode("WithResourceStoragePipsDecoration", ""); var storagePips = new MiniYamlNodeBuilder("WithResourceStoragePipsDecoration", "");
storagePips.AddNode("Position", "BottomLeft"); storagePips.AddNode("Position", "BottomLeft");
storagePips.AddNode("RequiresSelection", "true"); storagePips.AddNode("RequiresSelection", "true");
@@ -231,7 +231,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var pipCount = pipCountNode.NodeValue<int>(); var pipCount = pipCountNode.NodeValue<int>();
if (pipCount == 0) if (pipCount == 0)
{ {
addNodes.Add(new MiniYamlNode("-" + storagePips.Key, "")); addNodes.Add(new MiniYamlNodeBuilder("-" + storagePips.Key, ""));
continue; continue;
} }

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"Going forward, the value of the `Delay` attribute of the `DrawLineToTarget` trait will be\n" + "Going forward, the value of the `Delay` attribute of the `DrawLineToTarget` trait will be\n" +
"interpreted as milliseconds instead of ticks.\n"; "interpreted as milliseconds instead of ticks.\n";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var dltt in actorNode.ChildrenMatching("DrawLineToTarget", includeRemovals: false)) foreach (var dltt in actorNode.ChildrenMatching("DrawLineToTarget", includeRemovals: false))
{ {

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
static readonly string[] AffectedTraits = new string[] { "GrantExternalConditionPower", "ChronoshiftPower" }; static readonly string[] AffectedTraits = new string[] { "GrantExternalConditionPower", "ChronoshiftPower" };
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var at in AffectedTraits) foreach (var at in AffectedTraits)
foreach (var trait in actorNode.ChildrenMatching(at)) foreach (var trait in actorNode.ChildrenMatching(at))
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
void UpdatePower(MiniYamlNode power) void UpdatePower(MiniYamlNodeBuilder power)
{ {
var range = 1; var range = 1;
var rangeNode = power.LastChildMatching("Range"); var rangeNode = power.LastChildMatching("Range");
@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
var size = 2 * range + 1; var size = 2 * range + 1;
power.AddNode(new MiniYamlNode("Dimensions", size.ToString() + ", " + size.ToString())); power.AddNode(new MiniYamlNodeBuilder("Dimensions", size.ToString() + ", " + size.ToString()));
var footprint = string.Empty; var footprint = string.Empty;
@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
footprint += ' '; footprint += ' ';
} }
power.AddNode(new MiniYamlNode("Footprint", footprint)); power.AddNode(new MiniYamlNodeBuilder("Footprint", footprint));
} }
readonly List<string> locations = new(); readonly List<string> locations = new();

View File

@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
readonly List<Tuple<string, string, string>> weaponsToUpdate = new(); readonly List<Tuple<string, string, string>> weaponsToUpdate = new();
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var nukePowerTraits = actorNode.ChildrenMatching("NukePower"); var nukePowerTraits = actorNode.ChildrenMatching("NukePower");
foreach (var nukePowerTrait in nukePowerTraits) foreach (var nukePowerTrait in nukePowerTraits)

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var locationKey = $"{actorNode.Key} ({actorNode.Location.Filename})"; var locationKey = $"{actorNode.Key} ({actorNode.Location.Filename})";

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var bo in actorNode.ChildrenMatching("BodyOrientation")) foreach (var bo in actorNode.ChildrenMatching("BodyOrientation"))
{ {
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode sequenceNode) public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder sequenceNode)
{ {
foreach (var sequence in sequenceNode.Value.Nodes) foreach (var sequence in sequenceNode.Value.Nodes)
{ {

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "ConditionManager trait has been removed. Its functionality has been integrated into the actor itself."; public override string Description => "ConditionManager trait has been removed. Its functionality has been integrated into the actor itself.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RemoveNodes("ConditionManager"); actorNode.RemoveNodes("ConditionManager");
yield break; yield break;

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "'LaysTerrain' was removed."; public override string Description => "'LaysTerrain' was removed.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (actorNode.RemoveNodes("LaysTerrain") > 0) if (actorNode.RemoveNodes("LaysTerrain") > 0)
yield return $"'LaysTerrain' was removed from {actorNode.Key} ({actorNode.Location.Filename}) without replacement.\n"; yield return $"'LaysTerrain' was removed from {actorNode.Key} ({actorNode.Location.Filename}) without replacement.\n";

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"The same result can be created by using `Combine` in the sequence definitions to\n" + "The same result can be created by using `Combine` in the sequence definitions to\n" +
"assemble the different facings sprites into a single sequence."; "assemble the different facings sprites into a single sequence.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var a in actorNode.ChildrenMatching("Armament")) foreach (var a in actorNode.ChildrenMatching("Armament"))
{ {

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
turningAircraft.Clear(); turningAircraft.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var aircraft = actorNode.LastChildMatching("Aircraft"); var aircraft = actorNode.LastChildMatching("Aircraft");
if (aircraft != null) if (aircraft != null)

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "RenderDetectionCircle and RenderShroudCircle ContrastColor have been renamed to BorderColor for consistency."; public override string Description => "RenderDetectionCircle and RenderShroudCircle ContrastColor have been renamed to BorderColor for consistency.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var rdc in actorNode.ChildrenMatching("RenderDetectionCircle")) foreach (var rdc in actorNode.ChildrenMatching("RenderDetectionCircle"))
rdc.RenameChildrenMatching("ContrastColor", "BorderColor"); rdc.RenameChildrenMatching("ContrastColor", "BorderColor");

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The 'HealUnitsCrateAction' has been renamed to 'HealActorsCrateAction'."; public override string Description => "The 'HealUnitsCrateAction' has been renamed to 'HealActorsCrateAction'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var huca in actorNode.ChildrenMatching("HealUnitsCrateAction")) foreach (var huca in actorNode.ChildrenMatching("HealUnitsCrateAction"))
huca.RenameKey("HealActorsCrateAction"); huca.RenameKey("HealActorsCrateAction");

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The InfiltrateForCash Notification has been renamed to be in line with new notification properties added."; public override string Description => "The InfiltrateForCash Notification has been renamed to be in line with new notification properties added.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var rp in actorNode.ChildrenMatching("InfiltrateForCash")) foreach (var rp in actorNode.ChildrenMatching("InfiltrateForCash"))
rp.RenameChildrenMatching("Notification", "InfiltratedNotification"); rp.RenameChildrenMatching("Notification", "InfiltratedNotification");

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"SelfHealing was renamed to ChangesHealth\n" + "SelfHealing was renamed to ChangesHealth\n" +
"HealIfBelow was renamed to StartIfBelow."; "HealIfBelow was renamed to StartIfBelow.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var sh in actorNode.ChildrenMatching("SelfHealing")) foreach (var sh in actorNode.ChildrenMatching("SelfHealing"))
{ {

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"Renamed smoke-related properties on SmudgeLayer to be in line with comparable properties.\n" + "Renamed smoke-related properties on SmudgeLayer to be in line with comparable properties.\n" +
"Additionally, set the *Chance, *Image and *Sequences defaults to null."; "Additionally, set the *Chance, *Image and *Sequences defaults to null.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var layer in actorNode.ChildrenMatching("SmudgeLayer")) foreach (var layer in actorNode.ChildrenMatching("SmudgeLayer"))
{ {

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
("TooltipDescription", "ValidStances", "ValidRelationships") ("TooltipDescription", "ValidStances", "ValidRelationships")
}; };
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var (traitName, oldName, newName) in traits) foreach (var (traitName, oldName, newName) in traits)
foreach (var traitNode in actorNode.ChildrenMatching(traitName)) foreach (var traitNode in actorNode.ChildrenMatching(traitName))
@@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{ {
foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile")) foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile"))
projectileNode.RenameChildrenMatching("ValidBounceBlockerStances", "ValidBounceBlockerRelationships"); projectileNode.RenameChildrenMatching("ValidBounceBlockerStances", "ValidBounceBlockerRelationships");

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "`WithNukeLaunchAnimation` has been renamed to `WithSupportPowerActivationAnimation` and `WithNukeLaunchOverlay` to `WithSupportPowerActivationOverlay`."; public override string Description => "`WithNukeLaunchAnimation` has been renamed to `WithSupportPowerActivationAnimation` and `WithNukeLaunchOverlay` to `WithSupportPowerActivationOverlay`.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RenameChildrenMatching("WithNukeLaunchAnimation", "WithSupportPowerActivationAnimation", true); actorNode.RenameChildrenMatching("WithNukeLaunchAnimation", "WithSupportPowerActivationAnimation", true);
actorNode.RenameChildrenMatching("WithNukeLaunchOverlay", "WithSupportPowerActivationOverlay", true); actorNode.RenameChildrenMatching("WithNukeLaunchOverlay", "WithSupportPowerActivationOverlay", true);

View File

@@ -19,9 +19,9 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "Burns can be replaced using WithIdleOverlay and ChangesHealth."; public override string Description => "Burns can be replaced using WithIdleOverlay and ChangesHealth.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var addNodes = new List<MiniYamlNode>(); var addNodes = new List<MiniYamlNodeBuilder>();
foreach (var burns in actorNode.ChildrenMatching("Burns")) foreach (var burns in actorNode.ChildrenMatching("Burns"))
{ {
@@ -34,13 +34,13 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var interval = burns.LastChildMatching("Interval"); var interval = burns.LastChildMatching("Interval");
var intervalValue = interval != null ? interval.NodeValue<int>() : 8; var intervalValue = interval != null ? interval.NodeValue<int>() : 8;
var overlay = new MiniYamlNode("WithIdleOverlay@Burns", ""); var overlay = new MiniYamlNodeBuilder("WithIdleOverlay@Burns", "");
overlay.AddNode("Image", FieldSaver.FormatValue("fire")); overlay.AddNode("Image", FieldSaver.FormatValue("fire"));
overlay.AddNode("Sequence", FieldSaver.FormatValue(animValue)); overlay.AddNode("Sequence", FieldSaver.FormatValue(animValue));
overlay.AddNode("IsDecoration", FieldSaver.FormatValue(true)); overlay.AddNode("IsDecoration", FieldSaver.FormatValue(true));
addNodes.Add(overlay); addNodes.Add(overlay);
var changesHealth = new MiniYamlNode("ChangesHealth", ""); var changesHealth = new MiniYamlNodeBuilder("ChangesHealth", "");
changesHealth.AddNode("Step", FieldSaver.FormatValue(-damageValue)); changesHealth.AddNode("Step", FieldSaver.FormatValue(-damageValue));
changesHealth.AddNode("StartIfBelow", FieldSaver.FormatValue(101)); changesHealth.AddNode("StartIfBelow", FieldSaver.FormatValue(101));
changesHealth.AddNode("Delay", FieldSaver.FormatValue(intervalValue)); changesHealth.AddNode("Delay", FieldSaver.FormatValue(intervalValue));

View File

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var kv in TraitFields) foreach (var kv in TraitFields)
{ {
@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{ {
foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile")) foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile"))
{ {

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The 'EffectSequence' of 'SpawnActorPower' is unset by default."; public override string Description => "The 'EffectSequence' of 'SpawnActorPower' is unset by default.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var spawnActorPower in actorNode.ChildrenMatching("SpawnActorPower")) foreach (var spawnActorPower in actorNode.ChildrenMatching("SpawnActorPower"))
{ {

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "'DamageThreshold' and 'StartOnThreshold' are no longer supported and removed from 'DamagedByTerrain'."; public override string Description => "'DamageThreshold' and 'StartOnThreshold' are no longer supported and removed from 'DamagedByTerrain'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var damaged in actorNode.ChildrenMatching("DamagedByTerrain", includeRemovals: false)) foreach (var damaged in actorNode.ChildrenMatching("DamagedByTerrain", includeRemovals: false))
{ {

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
} }
public override IEnumerable<string> UpdateMapActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateMapActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (actorNode.RemoveNodes("Plugs") > 0) if (actorNode.RemoveNodes("Plugs") > 0)
yield return $"Initial plugs for actor {actorNode.Key} will need to be reconfigured using the map editor."; yield return $"Initial plugs for actor {actorNode.Key} will need to be reconfigured using the map editor.";
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
facing.ReplaceValue(FieldSaver.FormatValue(bodyFacing)); facing.ReplaceValue(FieldSaver.FormatValue(bodyFacing));
} }
var removeNodes = new List<MiniYamlNode>(); var removeNodes = new List<MiniYamlNodeBuilder>();
foreach (var facing in actorNode.ChildrenMatching("TurretFacing")) foreach (var facing in actorNode.ChildrenMatching("TurretFacing"))
{ {
var turretFacing = WAngle.FromFacing(facing.NodeValue<int>()) - bodyFacing; var turretFacing = WAngle.FromFacing(facing.NodeValue<int>()) - bodyFacing;
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
actorNode.Value.Nodes.Remove(node); actorNode.Value.Nodes.Remove(node);
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var turret in actorNode.ChildrenMatching("Turreted")) foreach (var turret in actorNode.ChildrenMatching("Turreted"))
turret.RemoveNodes("PreviewFacing"); turret.RemoveNodes("PreviewFacing");

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The LeftColor and RightColor keys in tilesets have been renamed to MinColor and MaxColor to reflect their proper usage."; public override string Description => "The LeftColor and RightColor keys in tilesets have been renamed to MinColor and MaxColor to reflect their proper usage.";
public override IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNode tilesetNode) public override IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNodeBuilder tilesetNode)
{ {
if (tilesetNode.Key == "Templates") if (tilesetNode.Key == "Templates")
{ {

View File

@@ -20,11 +20,11 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "A new trait ControlGroups was added, splitting logic away from Selection."; public override string Description => "A new trait ControlGroups was added, splitting logic away from Selection.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (actorNode.ChildrenMatching("Selection").Any(x => !x.IsRemoval()) if (actorNode.ChildrenMatching("Selection").Any(x => !x.IsRemoval())
&& !actorNode.ChildrenMatching("ControlGroups").Any()) && !actorNode.ChildrenMatching("ControlGroups").Any())
actorNode.AddNode(new MiniYamlNode("ControlGroups", "")); actorNode.AddNode(new MiniYamlNodeBuilder("ControlGroups", ""));
yield break; yield break;
} }

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The tolerance for attack angle was defined twice on AttackBomber. This override has to be defined in the rules now."; public override string Description => "The tolerance for attack angle was defined twice on AttackBomber. This override has to be defined in the rules now.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var attackBomber in actorNode.ChildrenMatching("AttackBomber", includeRemovals: false)) foreach (var attackBomber in actorNode.ChildrenMatching("AttackBomber", includeRemovals: false))
{ {
@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (facingTolerance != null) if (facingTolerance != null)
continue; continue;
var facingToleranceNode = new MiniYamlNode("FacingTolerance", FieldSaver.FormatValue(new WAngle(8))); var facingToleranceNode = new MiniYamlNodeBuilder("FacingTolerance", FieldSaver.FormatValue(new WAngle(8)));
attackBomber.AddNode(facingToleranceNode); attackBomber.AddNode(facingToleranceNode);
} }

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The tolerance for the attack angle was defined twice on AttackFrontal. This override has to be defined in the rules now."; public override string Description => "The tolerance for the attack angle was defined twice on AttackFrontal. This override has to be defined in the rules now.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var attackFrontal in actorNode.ChildrenMatching("AttackFrontal", includeRemovals: false)) foreach (var attackFrontal in actorNode.ChildrenMatching("AttackFrontal", includeRemovals: false))
{ {
@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (facingTolerance != null) if (facingTolerance != null)
continue; continue;
var facingToleranceNode = new MiniYamlNode("FacingTolerance", FieldSaver.FormatValue(WAngle.Zero)); var facingToleranceNode = new MiniYamlNodeBuilder("FacingTolerance", FieldSaver.FormatValue(WAngle.Zero));
attackFrontal.AddNode(facingToleranceNode); attackFrontal.AddNode(facingToleranceNode);
} }

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
readonly string[] traits = { "Interactable", "Selectable", "IsometricSelectable" }; readonly string[] traits = { "Interactable", "Selectable", "IsometricSelectable" };
readonly string[] fields = { "Bounds", "DecorationBounds" }; readonly string[] fields = { "Bounds", "DecorationBounds" };
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var grid = modData.Manifest.Get<MapGrid>(); var grid = modData.Manifest.Get<MapGrid>();
var tileSize = grid.TileSize; var tileSize = grid.TileSize;

View File

@@ -19,12 +19,12 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The DomainIndex trait was removed from World. Two overlay traits were added at the same time."; public override string Description => "The DomainIndex trait was removed from World. Two overlay traits were added at the same time.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (actorNode.RemoveNodes("DomainIndex") > 0) if (actorNode.RemoveNodes("DomainIndex") > 0)
{ {
actorNode.AddNode(new MiniYamlNode("PathFinderOverlay", "")); actorNode.AddNode(new MiniYamlNodeBuilder("PathFinderOverlay", ""));
actorNode.AddNode(new MiniYamlNode("HierarchicalPathFinderOverlay", "")); actorNode.AddNode(new MiniYamlNodeBuilder("HierarchicalPathFinderOverlay", ""));
} }
yield break; yield break;

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var removed = 0; var removed = 0;
foreach (var node in actorNode.ChildrenMatching("ActorPreviewPlaceBuildingPreview")) foreach (var node in actorNode.ChildrenMatching("ActorPreviewPlaceBuildingPreview"))

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "PlayerHighlightPalette trait has been removed. Its functionality is now automatically provided by the engine."; public override string Description => "PlayerHighlightPalette trait has been removed. Its functionality is now automatically provided by the engine.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RemoveNodes("PlayerHighlightPalette"); actorNode.RemoveNodes("PlayerHighlightPalette");
yield break; yield break;

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The Scale option was removed from RenderSprites. Scale can now be defined on individual sequence definitions."; public override string Description => "The Scale option was removed from RenderSprites. Scale can now be defined on individual sequence definitions.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var renderSprites in actorNode.ChildrenMatching("RenderSprites")) foreach (var renderSprites in actorNode.ChildrenMatching("RenderSprites"))
if (renderSprites.RemoveNodes("Scale") > 0) if (renderSprites.RemoveNodes("Scale") > 0)

View File

@@ -21,15 +21,15 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"The ResourceType trait has been removed, and resource definitions moved to the\n" + "The ResourceType trait has been removed, and resource definitions moved to the\n" +
"ResourceLayer, EditorResourceLayer, ResourceRenderer, and PlayerResources traits."; "ResourceLayer, EditorResourceLayer, ResourceRenderer, and PlayerResources traits.";
MiniYaml resourceLayer; MiniYamlBuilder resourceLayer;
MiniYaml resourceRenderer; MiniYamlBuilder resourceRenderer;
MiniYaml values; MiniYamlBuilder values;
public override IEnumerable<string> BeforeUpdate(ModData modData) public override IEnumerable<string> BeforeUpdate(ModData modData)
{ {
resourceLayer = new MiniYaml(""); resourceLayer = new MiniYamlBuilder("");
resourceRenderer = new MiniYaml(""); resourceRenderer = new MiniYamlBuilder("");
values = new MiniYaml(""); values = new MiniYamlBuilder("");
yield break; yield break;
} }
@@ -54,17 +54,17 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"You must define a custom ResourceLayer subclass if you want to customize the default behaviour."; "You must define a custom ResourceLayer subclass if you want to customize the default behaviour.";
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var resourceNode in actorNode.ChildrenMatching("ResourceType")) foreach (var resourceNode in actorNode.ChildrenMatching("ResourceType"))
{ {
var typeNode = resourceNode.LastChildMatching("Type"); var typeNode = resourceNode.LastChildMatching("Type");
if (typeNode != null) if (typeNode != null)
{ {
var resourceLayerNode = new MiniYamlNode(typeNode.Value.Value, ""); var resourceLayerNode = new MiniYamlNodeBuilder(new MiniYamlNode(typeNode.Value.Value, ""));
resourceLayer.Nodes.Add(resourceLayerNode); resourceLayer.Nodes.Add(resourceLayerNode);
var resourceRendererNode = new MiniYamlNode(typeNode.Value.Value, ""); var resourceRendererNode = new MiniYamlNodeBuilder(new MiniYamlNode(typeNode.Value.Value, ""));
resourceRenderer.Nodes.Add(resourceRendererNode); resourceRenderer.Nodes.Add(resourceRendererNode);
var indexNode = resourceNode.LastChildMatching("ResourceType"); var indexNode = resourceNode.LastChildMatching("ResourceType");
@@ -88,7 +88,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var valueNode = resourceNode.LastChildMatching("ValuePerUnit"); var valueNode = resourceNode.LastChildMatching("ValuePerUnit");
if (valueNode != null) if (valueNode != null)
values.Nodes.Add(new MiniYamlNode(typeNode.Value.Value, valueNode.Value.Value)); values.Nodes.Add(new MiniYamlNodeBuilder(typeNode.Value.Value, valueNode.Value.Value));
var imageNode = resourceNode.LastChildMatching("Image"); var imageNode = resourceNode.LastChildMatching("Image");
if (imageNode != null) if (imageNode != null)

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var locationKey = $"{actorNode.Key} ({actorNode.Location.Filename})"; var locationKey = $"{actorNode.Key} ({actorNode.Location.Filename})";
var anyConditionalSmokeTrail = false; var anyConditionalSmokeTrail = false;
@@ -94,7 +94,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (anyConditionalSmokeTrail) if (anyConditionalSmokeTrail)
{ {
var grantCondition = new MiniYamlNode("GrantConditionOnDamageState@SmokeTrail", ""); var grantCondition = new MiniYamlNodeBuilder("GrantConditionOnDamageState@SmokeTrail", "");
grantCondition.AddNode("Condition", FieldSaver.FormatValue("enable-smoke")); grantCondition.AddNode("Condition", FieldSaver.FormatValue("enable-smoke"));
actorNode.AddNode(grantCondition); actorNode.AddNode(grantCondition);
} }

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "Rename 'CloakTypes' to 'DetectionTypes' in order to make it clearer as well as make space for 'CloakType' in Cloak"; public override string Description => "Rename 'CloakTypes' to 'DetectionTypes' in order to make it clearer as well as make space for 'CloakType' in Cloak";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var traitNode in actorNode.ChildrenMatching("Cloak")) foreach (var traitNode in actorNode.ChildrenMatching("Cloak"))
traitNode.RenameChildrenMatching("CloakTypes", "DetectionTypes"); traitNode.RenameChildrenMatching("CloakTypes", "DetectionTypes");

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "Rename contrail color properties `Color` to `StartColor` and `UsePlayerColor` to `StartColorUsePlayerColor` in traits and weapons to account for added `EndColor` functionality"; public override string Description => "Rename contrail color properties `Color` to `StartColor` and `UsePlayerColor` to `StartColorUsePlayerColor` in traits and weapons to account for added `EndColor` functionality";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var traitNode in actorNode.ChildrenMatching("Contrail")) foreach (var traitNode in actorNode.ChildrenMatching("Contrail"))
{ {
@@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{ {
foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile")) foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile"))
{ {

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"'SpawnMPUnits' was renamed to 'SpawnStartingUnits', 'MPStartUnits' to 'StartingUnits', 'MPStartLocations' to " + "'SpawnMPUnits' was renamed to 'SpawnStartingUnits', 'MPStartUnits' to 'StartingUnits', 'MPStartLocations' to " +
"'MapStartingLocations', and 'CreateMPPlayers' to 'CreateMapPlayers'."; "'MapStartingLocations', and 'CreateMPPlayers' to 'CreateMapPlayers'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RenameChildrenMatching("SpawnMPUnits", "SpawnStartingUnits"); actorNode.RenameChildrenMatching("SpawnMPUnits", "SpawnStartingUnits");
actorNode.RenameChildrenMatching("MPStartUnits", "StartingUnits"); actorNode.RenameChildrenMatching("MPStartUnits", "StartingUnits");

View File

@@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Name => "Support powers now use 'Name' and 'Description' fields like units."; public override string Name => "Support powers now use 'Name' and 'Description' fields like units.";
public override string Description => "'Description' was renamed to 'Name' and 'LongDesc' was renamed to 'Description'."; public override string Description => "'Description' was renamed to 'Name' and 'LongDesc' was renamed to 'Description'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var traitNode in actorNode.ChildrenContaining("Power")) foreach (var traitNode in actorNode.ChildrenContaining("Power"))
{ {

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"by multiplying with 25 internally. Converted to use ticks like everything else.\n" + "by multiplying with 25 internally. Converted to use ticks like everything else.\n" +
"Also renamed Lifetime to Duration and ScaredyCat.PanicLength to PanicDuration to match other places."; "Also renamed Lifetime to Duration and ScaredyCat.PanicLength to PanicDuration to match other places.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var crateNode in actorNode.ChildrenMatching("Crate")) foreach (var crateNode in actorNode.ChildrenMatching("Crate"))
{ {

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The HarvesterResourceMultiplier trait has been removed, and the RefineryResourceMultiplier trait renamed to ResourceValueMultiplier."; public override string Description => "The HarvesterResourceMultiplier trait has been removed, and the RefineryResourceMultiplier trait renamed to ResourceValueMultiplier.";
bool notified; bool notified;
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (actorNode.RemoveNodes("HarvesterResourceModifier") > 0 && !notified) if (actorNode.RemoveNodes("HarvesterResourceModifier") > 0 && !notified)
{ {

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The EmbeddedPalette sequence option was replaced with a boolean HasEmbeddedPalette."; public override string Description => "The EmbeddedPalette sequence option was replaced with a boolean HasEmbeddedPalette.";
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode sequenceNode) public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder sequenceNode)
{ {
foreach (var sequence in sequenceNode.Value.Nodes) foreach (var sequence in sequenceNode.Value.Nodes)
if (sequence.RemoveNodes("EmbeddedPalette") > 0) if (sequence.RemoveNodes("EmbeddedPalette") > 0)

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{ {
foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile")) foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile"))
if (projectileNode.RemoveNodes("ShadowPalette") > 0) if (projectileNode.RemoveNodes("ShadowPalette") > 0)
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var node in actorNode.ChildrenMatching("WithShadow")) foreach (var node in actorNode.ChildrenMatching("WithShadow"))
if (node.RemoveNodes("Palette") > 0) if (node.RemoveNodes("Palette") > 0)

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var node in actorNode.ChildrenMatching("WithColoredOverlay")) foreach (var node in actorNode.ChildrenMatching("WithColoredOverlay"))
if (node.RemoveNodes("Palette") > 0) if (node.RemoveNodes("Palette") > 0)

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"NukePower used MissileWeapon field for as the name for missile image too.\n" + "NukePower used MissileWeapon field for as the name for missile image too.\n" +
"This function has been moved to its own MissileImage field."; "This function has been moved to its own MissileImage field.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var nukePowerNode in actorNode.ChildrenMatching("NukePower")) foreach (var nukePowerNode in actorNode.ChildrenMatching("NukePower"))
{ {
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (missileWeaponNode != null) if (missileWeaponNode != null)
{ {
var weapon = missileWeaponNode.NodeValue<string>(); var weapon = missileWeaponNode.NodeValue<string>();
nukePowerNode.AddNode(new MiniYamlNode("MissileImage", weapon)); nukePowerNode.AddNode(new MiniYamlNodeBuilder("MissileImage", weapon));
} }
} }

View File

@@ -16,7 +16,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
{ {
public class UnhardcodeBaseBuilderBotModule : UpdateRule, IBeforeUpdateActors public class UnhardcodeBaseBuilderBotModule : UpdateRule, IBeforeUpdateActors
{ {
MiniYamlNode defences; MiniYamlNodeBuilder defences;
// Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled. // Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled.
readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" }; readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" };
@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "DefenseTypes were added."; public override string Description => "DefenseTypes were added.";
public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors) public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{ {
var defences = new List<string>(); var defences = new List<string>();
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
} }
this.defences = new MiniYamlNode("DefenseTypes", FieldSaver.FormatValue(defences)); this.defences = new MiniYamlNodeBuilder("DefenseTypes", FieldSaver.FormatValue(defences));
yield break; yield break;
} }
@@ -83,7 +83,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
anyAdded = false; anyAdded = false;
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var squadManager in actorNode.ChildrenMatching("BaseBuilderBotModule", includeRemovals: false)) foreach (var squadManager in actorNode.ChildrenMatching("BaseBuilderBotModule", includeRemovals: false))
{ {

View File

@@ -16,7 +16,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
{ {
public class UnhardcodeSquadManager : UpdateRule, IBeforeUpdateActors public class UnhardcodeSquadManager : UpdateRule, IBeforeUpdateActors
{ {
readonly List<MiniYamlNode> addNodes = new(); readonly List<MiniYamlNodeBuilder> addNodes = new();
// Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled. // Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled.
readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" }; readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" };
@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "AirUnitsTypes and ProtectionTypes were added."; public override string Description => "AirUnitsTypes and ProtectionTypes were added.";
public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors) public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{ {
var aircraft = new List<string>(); var aircraft = new List<string>();
var vips = new List<string>(); var vips = new List<string>();
@@ -106,15 +106,15 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
} }
addNodes.Add(new MiniYamlNode("AirUnitsTypes", FieldSaver.FormatValue(aircraft))); addNodes.Add(new MiniYamlNodeBuilder("AirUnitsTypes", FieldSaver.FormatValue(aircraft)));
addNodes.Add(new MiniYamlNode("ProtectionTypes", FieldSaver.FormatValue(vips))); addNodes.Add(new MiniYamlNodeBuilder("ProtectionTypes", FieldSaver.FormatValue(vips)));
yield break; yield break;
} }
bool anyAdded = false; bool anyAdded = false;
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var squadManager in actorNode.ChildrenMatching("SquadManagerBotModule", includeRemovals: false)) foreach (var squadManager in actorNode.ChildrenMatching("SquadManagerBotModule", includeRemovals: false))
{ {

View File

@@ -32,12 +32,12 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var veteranProductionIconOverlay in actorNode.ChildrenMatching("VeteranProductionIconOverlay")) foreach (var veteranProductionIconOverlay in actorNode.ChildrenMatching("VeteranProductionIconOverlay"))
{ {
veteranProductionIconOverlay.RenameKey("ProductionIconOverlayManager"); veteranProductionIconOverlay.RenameKey("ProductionIconOverlayManager");
veteranProductionIconOverlay.AddNode(new MiniYamlNode("Type", "Veterancy")); veteranProductionIconOverlay.AddNode(new MiniYamlNodeBuilder("Type", "Veterancy"));
} }
foreach (var producibleWithLevel in actorNode.ChildrenMatching("ProducibleWithLevel")) foreach (var producibleWithLevel in actorNode.ChildrenMatching("ProducibleWithLevel"))

View File

@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"PowerManager.AdviceInterval and PlayerResources.InsufficientFundsNotificationDelay were using ticks.\n" + "PowerManager.AdviceInterval and PlayerResources.InsufficientFundsNotificationDelay were using ticks.\n" +
"Converted all of those to use real milliseconds instead."; "Converted all of those to use real milliseconds instead.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var announce in actorNode.ChildrenMatching("AnnounceOnKill")) foreach (var announce in actorNode.ChildrenMatching("AnnounceOnKill"))
{ {

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => public override string Description =>
"Each preset color can now have their brightness specified. SimilarityThreshold range was changed."; "Each preset color can now have their brightness specified. SimilarityThreshold range was changed.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var manager = actorNode.LastChildMatching("ColorPickerManager"); var manager = actorNode.LastChildMatching("ColorPickerManager");
if (manager == null) if (manager == null)

View File

@@ -26,18 +26,18 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"Tileset specific overrides can be defined as children of the TilesetFilenames field."; "Tileset specific overrides can be defined as children of the TilesetFilenames field.";
string defaultSpriteExtension = ".shp"; string defaultSpriteExtension = ".shp";
List<MiniYamlNode> resolvedImagesNodes; List<MiniYamlNodeBuilder> resolvedImagesNodes;
readonly Dictionary<string, string> tilesetExtensions = new(); readonly Dictionary<string, string> tilesetExtensions = new();
readonly Dictionary<string, string> tilesetCodes = new(); readonly Dictionary<string, string> tilesetCodes = new();
bool parseModYaml = true; bool parseModYaml = true;
bool reportModYamlChanges; bool reportModYamlChanges;
bool disabled; bool disabled;
public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNode> resolvedImagesNodes) public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNodeBuilder> resolvedImagesNodes)
{ {
// Keep a resolved copy of the sequences so we can account for values imported through inheritance or Defaults. // Keep a resolved copy of the sequences so we can account for values imported through inheritance or Defaults.
// This will be modified during processing, so take a deep copy to avoid side-effects on other update rules. // This will be modified during processing, so take a deep copy to avoid side-effects on other update rules.
this.resolvedImagesNodes = MiniYaml.FromString(resolvedImagesNodes.WriteToString()); this.resolvedImagesNodes = MiniYaml.FromString(resolvedImagesNodes.WriteToString()).Select(n => new MiniYamlNodeBuilder(n)).ToList();
var requiredMetadata = new HashSet<string>(); var requiredMetadata = new HashSet<string>();
foreach (var imageNode in resolvedImagesNodes) foreach (var imageNode in resolvedImagesNodes)
@@ -84,7 +84,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
var spriteSequenceFormatNode = new MiniYamlNode("", spriteSequenceFormatYaml); var spriteSequenceFormatNode = new MiniYamlNodeBuilder("", new MiniYamlBuilder(spriteSequenceFormatYaml));
var defaultSpriteExtensionNode = spriteSequenceFormatNode.LastChildMatching("DefaultSpriteExtension"); var defaultSpriteExtensionNode = spriteSequenceFormatNode.LastChildMatching("DefaultSpriteExtension");
if (defaultSpriteExtensionNode != null) if (defaultSpriteExtensionNode != null)
{ {
@@ -121,7 +121,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
reportModYamlChanges = false; reportModYamlChanges = false;
} }
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode imageNode) public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder imageNode)
{ {
if (disabled) if (disabled)
yield break; yield break;
@@ -154,7 +154,11 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (resolvedSequenceNode == resolvedDefaultsNode) if (resolvedSequenceNode == resolvedDefaultsNode)
continue; continue;
resolvedSequenceNode.Value.Nodes = MiniYaml.Merge(new[] { resolvedDefaultsNode.Value.Nodes, resolvedSequenceNode.Value.Nodes }); resolvedSequenceNode.Value.Nodes = MiniYaml.Merge(new[]
{
resolvedDefaultsNode.Value.Nodes.Select(n => n.Build()).ToArray(),
resolvedSequenceNode.Value.Nodes.Select(n => n.Build()).ToArray()
}).Select(n => new MiniYamlNodeBuilder(n)).ToList();
resolvedSequenceNode.Value.Value ??= resolvedDefaultsNode.Value.Value; resolvedSequenceNode.Value.Value ??= resolvedDefaultsNode.Value.Value;
} }
} }
@@ -180,8 +184,8 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
// Identify a suitable default for deduplication // Identify a suitable default for deduplication
MiniYamlNode defaultFilenameNode = null; MiniYamlNodeBuilder defaultFilenameNode = null;
MiniYamlNode defaultTilesetFilenamesNode = null; MiniYamlNodeBuilder defaultTilesetFilenamesNode = null;
foreach (var defaultsNode in imageNode.ChildrenMatching("Defaults")) foreach (var defaultsNode in imageNode.ChildrenMatching("Defaults"))
{ {
defaultFilenameNode = defaultsNode.LastChildMatching("Filename") ?? defaultFilenameNode; defaultFilenameNode = defaultsNode.LastChildMatching("Filename") ?? defaultFilenameNode;
@@ -222,12 +226,12 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var defaultsNode = imageNode.LastChildMatching("Defaults"); var defaultsNode = imageNode.LastChildMatching("Defaults");
if (defaultsNode == null) if (defaultsNode == null)
{ {
defaultsNode = new MiniYamlNode("Defaults", ""); defaultsNode = new MiniYamlNodeBuilder("Defaults", "");
imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode); imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode);
} }
var nodes = MiniYaml.FromString(duplicateTilesetCount.First(kv => kv.Value == maxDuplicateTilesetCount).Key); var nodes = MiniYaml.FromString(duplicateTilesetCount.First(kv => kv.Value == maxDuplicateTilesetCount).Key);
defaultTilesetFilenamesNode = new MiniYamlNode("TilesetFilenames", "", nodes); defaultTilesetFilenamesNode = new MiniYamlNodeBuilder("TilesetFilenames", "", nodes);
defaultsNode.Value.Nodes.Insert(0, defaultTilesetFilenamesNode); defaultsNode.Value.Nodes.Insert(0, defaultTilesetFilenamesNode);
} }
@@ -237,11 +241,11 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var defaultsNode = imageNode.LastChildMatching("Defaults"); var defaultsNode = imageNode.LastChildMatching("Defaults");
if (defaultsNode == null) if (defaultsNode == null)
{ {
defaultsNode = new MiniYamlNode("Defaults", ""); defaultsNode = new MiniYamlNodeBuilder("Defaults", "");
imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode); imageNode.Value.Nodes.Insert(inheritsNodeIndex, defaultsNode);
} }
defaultFilenameNode = new MiniYamlNode("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key); defaultFilenameNode = new MiniYamlNodeBuilder("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key);
defaultsNode.Value.Nodes.Insert(0, defaultFilenameNode); defaultsNode.Value.Nodes.Insert(0, defaultFilenameNode);
} }
} }
@@ -257,15 +261,15 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames"); var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames");
if (defaultTilesetFilenamesNode != null && combineNode != null) if (defaultTilesetFilenamesNode != null && combineNode != null)
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNode("TilesetFilenames", "")); sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("TilesetFilenames", ""));
if (defaultFilenameNode != null && combineNode != null) if (defaultFilenameNode != null && combineNode != null)
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNode("Filename", "")); sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("Filename", ""));
if (defaultTilesetFilenamesNode != null && tilesetFilenamesNode == null && filenameNode != null) if (defaultTilesetFilenamesNode != null && tilesetFilenamesNode == null && filenameNode != null)
{ {
var index = sequenceNode.Value.Nodes.IndexOf(filenameNode) + 1; var index = sequenceNode.Value.Nodes.IndexOf(filenameNode) + 1;
sequenceNode.Value.Nodes.Insert(index, new MiniYamlNode("TilesetFilenames", "")); sequenceNode.Value.Nodes.Insert(index, new MiniYamlNodeBuilder("TilesetFilenames", ""));
} }
// Remove redundant overrides // Remove redundant overrides
@@ -334,7 +338,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
imageNode.RemoveNode(sequenceNode); imageNode.RemoveNode(sequenceNode);
} }
void ProcessNode(ModData modData, MiniYamlNode sequenceNode, MiniYamlNode resolvedSequenceNode, string imageName) void ProcessNode(ModData modData, MiniYamlNodeBuilder sequenceNode, MiniYamlNodeBuilder resolvedSequenceNode, string imageName)
{ {
// "Filename" was introduced with this update rule, so that means this node was already processed and can be skipped // "Filename" was introduced with this update rule, so that means this node was already processed and can be skipped
if (sequenceNode.LastChildMatching("Filename") != null) if (sequenceNode.LastChildMatching("Filename") != null)
@@ -390,7 +394,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (useTilesetExtension || useTilesetCode) if (useTilesetExtension || useTilesetCode)
{ {
var tilesetFilenamesNode = new MiniYamlNode("TilesetFilenames", ""); var tilesetFilenamesNode = new MiniYamlNodeBuilder("TilesetFilenames", "");
var duplicateCount = new Dictionary<string, int>(); var duplicateCount = new Dictionary<string, int>();
foreach (var tileset in modData.DefaultTerrainInfo.Keys) foreach (var tileset in modData.DefaultTerrainInfo.Keys)
{ {
@@ -415,7 +419,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var maxDuplicateCount = duplicateCount.MaxByOrDefault(kv => kv.Value).Value; var maxDuplicateCount = duplicateCount.MaxByOrDefault(kv => kv.Value).Value;
if (maxDuplicateCount > 1) if (maxDuplicateCount > 1)
{ {
var filenameNode = new MiniYamlNode("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key); var filenameNode = new MiniYamlNodeBuilder("Filename", duplicateCount.First(kv => kv.Value == maxDuplicateCount).Key);
foreach (var overrideNode in tilesetFilenamesNode.Value.Nodes.ToList()) foreach (var overrideNode in tilesetFilenamesNode.Value.Nodes.ToList())
if (overrideNode.Value.Value == filenameNode.Value.Value) if (overrideNode.Value.Value == filenameNode.Value.Value)
tilesetFilenamesNode.Value.Nodes.Remove(overrideNode); tilesetFilenamesNode.Value.Nodes.Remove(overrideNode);
@@ -431,7 +435,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (addExtension) if (addExtension)
filename += defaultSpriteExtension; filename += defaultSpriteExtension;
sequenceNode.Value.Nodes.Insert(0, new MiniYamlNode("Filename", filename)); sequenceNode.Value.Nodes.Insert(0, new MiniYamlNodeBuilder("Filename", filename));
} }
sequenceNode.ReplaceValue(""); sequenceNode.ReplaceValue("");

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => public override string Description =>
"Change the field name from Button to TabButton and add ArrowButton, if Button field was set."; "Change the field name from Button to TabButton and add ArrowButton, if Button field was set.";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNode chromeNode) public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{ {
if (!chromeNode.KeyMatches("ProductionTabs")) if (!chromeNode.KeyMatches("ProductionTabs"))
yield break; yield break;
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
} }
if (buttonCollection != null) if (buttonCollection != null)
chromeNode.AddNode(new MiniYamlNode("ArrowButton", buttonCollection)); chromeNode.AddNode(new MiniYamlNodeBuilder("ArrowButton", buttonCollection));
} }
} }
} }

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
var removed = false; var removed = false;
foreach (var node in actorNode.ChildrenMatching("Infiltrates")) foreach (var node in actorNode.ChildrenMatching("Infiltrates"))

View File

@@ -21,9 +21,9 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "Negative sequence length is no longer allowed, define individual frames in reverse instead."; public override string Description => "Negative sequence length is no longer allowed, define individual frames in reverse instead.";
List<MiniYamlNode> resolvedImagesNodes; List<MiniYamlNodeBuilder> resolvedImagesNodes;
public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNode> resolvedImagesNodes) public IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNodeBuilder> resolvedImagesNodes)
{ {
this.resolvedImagesNodes = resolvedImagesNodes; this.resolvedImagesNodes = resolvedImagesNodes;
yield break; yield break;
@@ -31,12 +31,12 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
readonly Queue<Action> actionQueue = new(); readonly Queue<Action> actionQueue = new();
static MiniYamlNode GetNode(string key, MiniYamlNode node, MiniYamlNode defaultNode) static MiniYamlNodeBuilder GetNode(string key, MiniYamlNodeBuilder node, MiniYamlNodeBuilder defaultNode)
{ {
return node.LastChildMatching(key, includeRemovals: false) ?? defaultNode?.LastChildMatching(key, includeRemovals: false); return node.LastChildMatching(key, includeRemovals: false) ?? defaultNode?.LastChildMatching(key, includeRemovals: false);
} }
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode sequenceNode) public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder sequenceNode)
{ {
var defaultNode = sequenceNode.LastChildMatching("Defaults"); var defaultNode = sequenceNode.LastChildMatching("Defaults");
var defaultLengthNode = defaultNode == null ? null : GetNode("Length", defaultNode, null); var defaultLengthNode = defaultNode == null ? null : GetNode("Length", defaultNode, null);

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
locations.Clear(); locations.Clear();
} }
public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode imageNode) public override IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder imageNode)
{ {
foreach (var sequenceNode in imageNode.Value.Nodes) foreach (var sequenceNode in imageNode.Value.Nodes)
sequenceNode.RemoveNodes("HasEmbeddedPalette"); sequenceNode.RemoveNodes("HasEmbeddedPalette");
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var traitNode in actorNode.ChildrenMatching("PaletteFromEmbeddedSpritePalette")) foreach (var traitNode in actorNode.ChildrenMatching("PaletteFromEmbeddedSpritePalette"))
{ {

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "TiberianSunRefinery was removed, use Refinery instead."; public override string Description => "TiberianSunRefinery was removed, use Refinery instead.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RenameChildrenMatching("TiberianSunRefinery", "Refinery"); actorNode.RenameChildrenMatching("TiberianSunRefinery", "Refinery");

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "Rename contrail `TrailWidth` to `StartWidth` in traits and weapons to acount for added `EndWidth` functionality"; public override string Description => "Rename contrail `TrailWidth` to `StartWidth` in traits and weapons to acount for added `EndWidth` functionality";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var traitNode in actorNode.ChildrenMatching("Contrail")) foreach (var traitNode in actorNode.ChildrenMatching("Contrail"))
traitNode.RenameChildrenMatching("TrailWidth", "StartWidth"); traitNode.RenameChildrenMatching("TrailWidth", "StartWidth");
@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode)
{ {
foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile")) foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile"))
traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth"); traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth");

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"'EngineerRepair' was renamed to 'InstantlyRepairs' " + "'EngineerRepair' was renamed to 'InstantlyRepairs' " +
"and 'EngineerRepairable' to 'InstantlyRepairable'."; "and 'EngineerRepairable' to 'InstantlyRepairable'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
actorNode.RenameChildrenMatching("EngineerRepair", "InstantlyRepairs"); actorNode.RenameChildrenMatching("EngineerRepair", "InstantlyRepairs");
actorNode.RenameChildrenMatching("EngineerRepairable", "InstantlyRepairable"); actorNode.RenameChildrenMatching("EngineerRepairable", "InstantlyRepairable");

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => "The 'GiveMcvCrateAction' has been renamed to 'GiveBaseBuilderCrateAction'."; public override string Description => "The 'GiveMcvCrateAction' has been renamed to 'GiveBaseBuilderCrateAction'.";
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
foreach (var node in actorNode.ChildrenMatching("GiveMcvCrateAction")) foreach (var node in actorNode.ChildrenMatching("GiveMcvCrateAction"))
node.RenameKey("GiveBaseBuilderCrateAction"); node.RenameKey("GiveBaseBuilderCrateAction");

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override string Description => public override string Description =>
"Change the field name from RemoveTime to DisplayDurationMs and convert the value from ticks to milliseconds"; "Change the field name from RemoveTime to DisplayDurationMs and convert the value from ticks to milliseconds";
public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNode chromeNode) public override IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode)
{ {
if (!chromeNode.KeyMatches("TextNotificationsDisplay")) if (!chromeNode.KeyMatches("TextNotificationsDisplay"))
yield break; yield break;

View File

@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break; yield break;
} }
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{ {
if (complete || actorNode.LastChildMatching("IsometricSelectable") != null) if (complete || actorNode.LastChildMatching("IsometricSelectable") != null)
yield break; yield break;
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (height == 24) if (height == 24)
yield break; yield break;
var selection = new MiniYamlNode("IsometricSelectable", ""); var selection = new MiniYamlNodeBuilder("IsometricSelectable", "");
selection.AddNode("Height", FieldSaver.FormatValue(height)); selection.AddNode("Height", FieldSaver.FormatValue(height));
actorNode.AddNode(selection); actorNode.AddNode(selection);

View File

@@ -20,19 +20,19 @@ namespace OpenRA.Mods.Common.UpdateRules
/// <summary>Defines a transformation that is run on each top-level node in a yaml file set.</summary> /// <summary>Defines a transformation that is run on each top-level node in a yaml file set.</summary>
/// <returns>An enumerable of manual steps to be run by the user.</returns> /// <returns>An enumerable of manual steps to be run by the user.</returns>
public delegate IEnumerable<string> TopLevelNodeTransform(ModData modData, MiniYamlNode node); public delegate IEnumerable<string> TopLevelNodeTransform(ModData modData, MiniYamlNodeBuilder node);
/// <summary>Defines a transformation that is run on each widget node in a chrome yaml file set.</summary> /// <summary>Defines a transformation that is run on each widget node in a chrome yaml file set.</summary>
/// <returns>An enumerable of manual steps to be run by the user.</returns> /// <returns>An enumerable of manual steps to be run by the user.</returns>
public delegate IEnumerable<string> ChromeNodeTransform(ModData modData, MiniYamlNode widgetNode); public delegate IEnumerable<string> ChromeNodeTransform(ModData modData, MiniYamlNodeBuilder widgetNode);
public virtual IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode) { yield break; } public virtual IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode) { yield break; }
public virtual IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) { yield break; } public virtual IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNodeBuilder weaponNode) { yield break; }
public virtual IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNode sequenceNode) { yield break; } public virtual IEnumerable<string> UpdateSequenceNode(ModData modData, MiniYamlNodeBuilder sequenceNode) { yield break; }
public virtual IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNode chromeNode) { yield break; } public virtual IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNodeBuilder chromeNode) { yield break; }
public virtual IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNode tilesetNode) { yield break; } public virtual IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNodeBuilder tilesetNode) { yield break; }
public virtual IEnumerable<string> UpdateChromeProviderNode(ModData modData, MiniYamlNode chromeProviderNode) { yield break; } public virtual IEnumerable<string> UpdateChromeProviderNode(ModData modData, MiniYamlNodeBuilder chromeProviderNode) { yield break; }
public virtual IEnumerable<string> UpdateMapActorNode(ModData modData, MiniYamlNode actorNode) { yield break; } public virtual IEnumerable<string> UpdateMapActorNode(ModData modData, MiniYamlNodeBuilder actorNode) { yield break; }
public virtual IEnumerable<string> BeforeUpdate(ModData modData) { yield break; } public virtual IEnumerable<string> BeforeUpdate(ModData modData) { yield break; }
public virtual IEnumerable<string> AfterUpdate(ModData modData) { yield break; } public virtual IEnumerable<string> AfterUpdate(ModData modData) { yield break; }
@@ -41,16 +41,16 @@ namespace OpenRA.Mods.Common.UpdateRules
// These aren't part of the UpdateRule class as to avoid premature yaml merge crashes when updating maps. // These aren't part of the UpdateRule class as to avoid premature yaml merge crashes when updating maps.
public interface IBeforeUpdateActors public interface IBeforeUpdateActors
{ {
IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors) { yield break; } IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors) { yield break; }
} }
public interface IBeforeUpdateWeapons public interface IBeforeUpdateWeapons
{ {
IEnumerable<string> BeforeUpdateWeapons(ModData modData, List<MiniYamlNode> resolvedWeapons) { yield break; } IEnumerable<string> BeforeUpdateWeapons(ModData modData, List<MiniYamlNodeBuilder> resolvedWeapons) { yield break; }
} }
public interface IBeforeUpdateSequences public interface IBeforeUpdateSequences
{ {
IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNode> resolvedImages) { yield break; } IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNodeBuilder> resolvedImages) { yield break; }
} }
} }

View File

@@ -17,7 +17,7 @@ using OpenRA.FileSystem;
namespace OpenRA.Mods.Common.UpdateRules namespace OpenRA.Mods.Common.UpdateRules
{ {
using YamlFileSet = List<(IReadWritePackage, string, List<MiniYamlNode>)>; using YamlFileSet = List<(IReadWritePackage, string, List<MiniYamlNodeBuilder>)>;
public static class UpdateUtils public static class UpdateUtils
{ {
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.UpdateRules
continue; continue;
} }
yaml.Add(((IReadWritePackage)package, name, MiniYaml.FromStream(package.GetStream(name), name, false))); yaml.Add(((IReadWritePackage)package, name, MiniYaml.FromStream(package.GetStream(name), name, false).Select(n => new MiniYamlNodeBuilder(n)).ToList()));
} }
return yaml; return yaml;
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.UpdateRules
/// <summary> /// <summary>
/// Loads a YamlFileSet containing any external yaml definitions referenced by a map yaml block. /// Loads a YamlFileSet containing any external yaml definitions referenced by a map yaml block.
/// </summary> /// </summary>
static YamlFileSet LoadExternalMapYaml(ModData modData, MiniYaml yaml, HashSet<string> externalFilenames) static YamlFileSet LoadExternalMapYaml(ModData modData, MiniYamlBuilder yaml, HashSet<string> externalFilenames)
{ {
return FieldLoader.GetValue<string[]>("value", yaml.Value) return FieldLoader.GetValue<string[]>("value", yaml.Value)
.Where(f => f.Contains('|')) .Where(f => f.Contains('|'))
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.Common.UpdateRules
/// Loads a YamlFileSet containing any internal definitions yaml referenced by a map yaml block. /// Loads a YamlFileSet containing any internal definitions yaml referenced by a map yaml block.
/// External references or internal references to missing files are ignored. /// External references or internal references to missing files are ignored.
/// </summary> /// </summary>
static YamlFileSet LoadInternalMapYaml(ModData modData, IReadWritePackage mapPackage, MiniYaml yaml, HashSet<string> externalFilenames) static YamlFileSet LoadInternalMapYaml(ModData modData, IReadWritePackage mapPackage, MiniYamlBuilder yaml, HashSet<string> externalFilenames)
{ {
var fileSet = new YamlFileSet() var fileSet = new YamlFileSet()
{ {
@@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.UpdateRules
{ {
// Ignore any files that aren't in the map bundle // Ignore any files that aren't in the map bundle
if (!filename.Contains('|') && mapPackage.Contains(filename)) if (!filename.Contains('|') && mapPackage.Contains(filename))
fileSet.Add((mapPackage, filename, MiniYaml.FromStream(mapPackage.GetStream(filename), filename, false))); fileSet.Add((mapPackage, filename, MiniYaml.FromStream(mapPackage.GetStream(filename), filename, false).Select(n => new MiniYamlNodeBuilder(n)).ToList()));
else if (modData.ModFiles.Exists(filename)) else if (modData.ModFiles.Exists(filename))
externalFilenames.Add(filename); externalFilenames.Add(filename);
} }
@@ -94,7 +94,7 @@ namespace OpenRA.Mods.Common.UpdateRules
return manualSteps; return manualSteps;
} }
var yaml = new MiniYaml(null, MiniYaml.FromStream(mapStream, mapPackage.Name, false)); var yaml = new MiniYamlBuilder(null, MiniYaml.FromStream(mapStream, mapPackage.Name, false));
files = new YamlFileSet() { (mapPackage, "map.yaml", yaml.Nodes) }; files = new YamlFileSet() { (mapPackage, "map.yaml", yaml.Nodes) };
manualSteps.AddRange(rule.BeforeUpdate(modData)); manualSteps.AddRange(rule.BeforeUpdate(modData));
@@ -159,7 +159,7 @@ namespace OpenRA.Mods.Common.UpdateRules
return manualSteps; return manualSteps;
} }
public static List<MiniYamlNode> LoadMapYaml(IReadOnlyFileSystem fileSystem, IReadOnlyPackage mapPackage, IEnumerable<string> files, MiniYaml mapNode) public static List<MiniYamlNodeBuilder> LoadMapYaml(IReadOnlyFileSystem fileSystem, IReadOnlyPackage mapPackage, IEnumerable<string> files, MiniYamlBuilder mapNode)
{ {
var yaml = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)).ToList(); var yaml = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)).ToList();
@@ -177,9 +177,9 @@ namespace OpenRA.Mods.Common.UpdateRules
} }
if (mapNode != null && mapNode.Nodes.Count > 0) if (mapNode != null && mapNode.Nodes.Count > 0)
yaml.Add(mapNode.Nodes); yaml.Add(mapNode.Nodes.Select(n => n.Build()).ToList());
return MiniYaml.Merge(yaml); return MiniYaml.Merge(yaml).Select(n => new MiniYamlNodeBuilder(n)).ToList();
} }
static IEnumerable<string> FilterExternalModFiles(ModData modData, IEnumerable<string> files, HashSet<string> externalFilenames) static IEnumerable<string> FilterExternalModFiles(ModData modData, IEnumerable<string> files, HashSet<string> externalFilenames)
@@ -215,7 +215,7 @@ namespace OpenRA.Mods.Common.UpdateRules
if (mapStream == null) if (mapStream == null)
continue; continue;
var yaml = new MiniYaml(null, MiniYaml.FromStream(mapStream, package.Name, false)); 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.Nodes.FirstOrDefault(n => n.Key == "Rules");
if (mapRulesNode != null) if (mapRulesNode != null)
foreach (var f in LoadExternalMapYaml(modData, mapRulesNode.Value, externalFilenames)) foreach (var f in LoadExternalMapYaml(modData, mapRulesNode.Value, externalFilenames))
@@ -240,7 +240,8 @@ namespace OpenRA.Mods.Common.UpdateRules
if (rule is IBeforeUpdateActors beforeActors) if (rule is IBeforeUpdateActors beforeActors)
{ {
var resolvedActors = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Rules, null); var resolvedActors = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Rules, null)
.Select(n => new MiniYamlNodeBuilder(n)).ToList();
manualSteps.AddRange(beforeActors.BeforeUpdateActors(modData, resolvedActors)); manualSteps.AddRange(beforeActors.BeforeUpdateActors(modData, resolvedActors));
} }
@@ -248,7 +249,8 @@ namespace OpenRA.Mods.Common.UpdateRules
if (rule is IBeforeUpdateWeapons beforeWeapons) if (rule is IBeforeUpdateWeapons beforeWeapons)
{ {
var resolvedWeapons = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Weapons, null); var resolvedWeapons = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Weapons, null)
.Select(n => new MiniYamlNodeBuilder(n)).ToList();
manualSteps.AddRange(beforeWeapons.BeforeUpdateWeapons(modData, resolvedWeapons)); manualSteps.AddRange(beforeWeapons.BeforeUpdateWeapons(modData, resolvedWeapons));
} }
@@ -256,7 +258,8 @@ namespace OpenRA.Mods.Common.UpdateRules
if (rule is IBeforeUpdateSequences beforeSequences) if (rule is IBeforeUpdateSequences beforeSequences)
{ {
var resolvedImages = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Sequences, null); var resolvedImages = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Sequences, null)
.Select(n => new MiniYamlNodeBuilder(n)).ToList();
manualSteps.AddRange(beforeSequences.BeforeUpdateSequences(modData, resolvedImages)); manualSteps.AddRange(beforeSequences.BeforeUpdateSequences(modData, resolvedImages));
} }
@@ -277,7 +280,7 @@ namespace OpenRA.Mods.Common.UpdateRules
return manualSteps; return manualSteps;
} }
static IEnumerable<string> ApplyChromeTransformInner(ModData modData, MiniYamlNode current, UpdateRule.ChromeNodeTransform transform) static IEnumerable<string> ApplyChromeTransformInner(ModData modData, MiniYamlNodeBuilder current, UpdateRule.ChromeNodeTransform transform)
{ {
foreach (var manualStep in transform(modData, current)) foreach (var manualStep in transform(modData, current))
yield return manualStep; yield return manualStep;
@@ -337,13 +340,13 @@ namespace OpenRA.Mods.Common.UpdateRules
} }
/// <summary>Checks if node is a removal (has '-' prefix).</summary> /// <summary>Checks if node is a removal (has '-' prefix).</summary>
public static bool IsRemoval(this MiniYamlNode node) public static bool IsRemoval(this MiniYamlNodeBuilder node)
{ {
return node.Key[0].ToString() == "-"; return node.Key[0].ToString() == "-";
} }
/// <summary>Renames a yaml key preserving any @suffix.</summary> /// <summary>Renames a yaml key preserving any @suffix.</summary>
public static void RenameKey(this MiniYamlNode node, string newKey, bool preserveSuffix = true, bool includeRemovals = true) public static void RenameKey(this MiniYamlNodeBuilder node, string newKey, bool preserveSuffix = true, bool includeRemovals = true)
{ {
var prefix = includeRemovals && node.IsRemoval() ? "-" : ""; var prefix = includeRemovals && node.IsRemoval() ? "-" : "";
var split = node.Key.IndexOf("@", StringComparison.Ordinal); var split = node.Key.IndexOf("@", StringComparison.Ordinal);
@@ -353,52 +356,52 @@ namespace OpenRA.Mods.Common.UpdateRules
node.Key = prefix + newKey; node.Key = prefix + newKey;
} }
public static T NodeValue<T>(this MiniYamlNode node) public static T NodeValue<T>(this MiniYamlNodeBuilder node)
{ {
return FieldLoader.GetValue<T>(node.Key, node.Value.Value); return FieldLoader.GetValue<T>(node.Key, node.Value.Value);
} }
public static void ReplaceValue(this MiniYamlNode node, string value) public static void ReplaceValue(this MiniYamlNodeBuilder node, string value)
{ {
node.Value.Value = value; node.Value.Value = value;
} }
public static void AddNode(this MiniYamlNode node, string key, object value) public static void AddNode(this MiniYamlNodeBuilder node, string key, object value)
{ {
node.Value.Nodes.Add(new MiniYamlNode(key, FieldSaver.FormatValue(value))); node.Value.Nodes.Add(new MiniYamlNodeBuilder(key, FieldSaver.FormatValue(value)));
} }
public static void AddNode(this MiniYamlNode node, MiniYamlNode toAdd) public static void AddNode(this MiniYamlNodeBuilder node, MiniYamlNodeBuilder toAdd)
{ {
node.Value.Nodes.Add(toAdd); node.Value.Nodes.Add(toAdd);
} }
public static void RemoveNode(this MiniYamlNode node, MiniYamlNode toRemove) public static void RemoveNode(this MiniYamlNodeBuilder node, MiniYamlNodeBuilder toRemove)
{ {
node.Value.Nodes.Remove(toRemove); node.Value.Nodes.Remove(toRemove);
} }
public static void MoveNode(this MiniYamlNode node, MiniYamlNode fromNode, MiniYamlNode toNode) public static void MoveNode(this MiniYamlNodeBuilder node, MiniYamlNodeBuilder fromNode, MiniYamlNodeBuilder toNode)
{ {
toNode.Value.Nodes.Add(node); toNode.Value.Nodes.Add(node);
fromNode.Value.Nodes.Remove(node); fromNode.Value.Nodes.Remove(node);
} }
public static void MoveAndRenameNode(this MiniYamlNode node, public static void MoveAndRenameNode(this MiniYamlNodeBuilder node,
MiniYamlNode fromNode, MiniYamlNode toNode, string newKey, bool preserveSuffix = true, bool includeRemovals = true) MiniYamlNodeBuilder fromNode, MiniYamlNodeBuilder toNode, string newKey, bool preserveSuffix = true, bool includeRemovals = true)
{ {
node.RenameKey(newKey, preserveSuffix, includeRemovals); node.RenameKey(newKey, preserveSuffix, includeRemovals);
node.MoveNode(fromNode, toNode); node.MoveNode(fromNode, toNode);
} }
/// <summary>Removes children with keys equal to [match] or [match]@[arbitrary suffix].</summary> /// <summary>Removes children with keys equal to [match] or [match]@[arbitrary suffix].</summary>
public static int RemoveNodes(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true) public static int RemoveNodes(this MiniYamlNodeBuilder node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
{ {
return node.Value.Nodes.RemoveAll(n => n.KeyMatches(match, ignoreSuffix, includeRemovals)); return node.Value.Nodes.RemoveAll(n => n.KeyMatches(match, ignoreSuffix, includeRemovals));
} }
/// <summary>Returns true if the node is of the form [match] or [match]@[arbitrary suffix].</summary> /// <summary>Returns true if the node is of the form [match] or [match]@[arbitrary suffix].</summary>
public static bool KeyMatches(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true) public static bool KeyMatches(this MiniYamlNodeBuilder node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
{ {
if (node.Key == null) if (node.Key == null)
return false; return false;
@@ -416,7 +419,7 @@ namespace OpenRA.Mods.Common.UpdateRules
} }
/// <summary>Returns true if the node is of the form [match], [match]@[arbitrary suffix] or [arbitrary suffix]@[match].</summary> /// <summary>Returns true if the node is of the form [match], [match]@[arbitrary suffix] or [arbitrary suffix]@[match].</summary>
public static bool KeyContains(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true) public static bool KeyContains(this MiniYamlNodeBuilder node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
{ {
if (node.Key == null) if (node.Key == null)
return false; return false;
@@ -431,23 +434,23 @@ namespace OpenRA.Mods.Common.UpdateRules
} }
/// <summary>Returns children with keys equal to [match] or [match]@[arbitrary suffix].</summary> /// <summary>Returns children with keys equal to [match] or [match]@[arbitrary suffix].</summary>
public static IEnumerable<MiniYamlNode> ChildrenMatching(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true) public static IEnumerable<MiniYamlNodeBuilder> ChildrenMatching(this MiniYamlNodeBuilder node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
{ {
return node.Value.Nodes.Where(n => n.KeyMatches(match, ignoreSuffix, includeRemovals)); return node.Value.Nodes.Where(n => n.KeyMatches(match, ignoreSuffix, includeRemovals));
} }
/// <summary>Returns children whose keys contain 'match' (optionally in the suffix).</summary> /// <summary>Returns children whose keys contain 'match' (optionally in the suffix).</summary>
public static IEnumerable<MiniYamlNode> ChildrenContaining(this MiniYamlNode node, string match, bool ignoreSuffix = true, bool includeRemovals = true) public static IEnumerable<MiniYamlNodeBuilder> ChildrenContaining(this MiniYamlNodeBuilder node, string match, bool ignoreSuffix = true, bool includeRemovals = true)
{ {
return node.Value.Nodes.Where(n => n.KeyContains(match, ignoreSuffix, includeRemovals)); return node.Value.Nodes.Where(n => n.KeyContains(match, ignoreSuffix, includeRemovals));
} }
public static MiniYamlNode LastChildMatching(this MiniYamlNode node, string match, bool includeRemovals = true) public static MiniYamlNodeBuilder LastChildMatching(this MiniYamlNodeBuilder node, string match, bool includeRemovals = true)
{ {
return node.ChildrenMatching(match, includeRemovals: includeRemovals).LastOrDefault(); return node.ChildrenMatching(match, includeRemovals: includeRemovals).LastOrDefault();
} }
public static void RenameChildrenMatching(this MiniYamlNode node, string match, string newKey, bool preserveSuffix = true, bool includeRemovals = true) public static void RenameChildrenMatching(this MiniYamlNodeBuilder node, string match, string newKey, bool preserveSuffix = true, bool includeRemovals = true)
{ {
var matching = node.ChildrenMatching(match); var matching = node.ChildrenMatching(match);
foreach (var m in matching) foreach (var m in matching)

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
namespace OpenRA.Mods.Common.UtilityCommands namespace OpenRA.Mods.Common.UtilityCommands
@@ -68,8 +69,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
} }
} }
foreach (var kv in forRemoval) map.ActorDefinitions = map.ActorDefinitions.Except(forRemoval).ToArray();
map.ActorDefinitions.Remove(kv);
map.Save((IReadWritePackage)map.Package); map.Save((IReadWritePackage)map.Package);
} }

View File

@@ -18,7 +18,7 @@ using OpenRA.Mods.Common.UpdateRules;
namespace OpenRA.Mods.Common.UtilityCommands namespace OpenRA.Mods.Common.UtilityCommands
{ {
using YamlFileSet = List<(IReadWritePackage, string, List<MiniYamlNode>)>; using YamlFileSet = List<(IReadWritePackage, string, List<MiniYamlNodeBuilder>)>;
sealed class UpdateModCommand : IUtilityCommand sealed class UpdateModCommand : IUtilityCommand
{ {

Some files were not shown because too many files have changed in this diff Show More