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

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

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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 trait in actorNode.ChildrenMatching(at))
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
yield break;
}
void UpdatePower(MiniYamlNode power)
void UpdatePower(MiniYamlNodeBuilder power)
{
var range = 1;
var rangeNode = power.LastChildMatching("Range");
@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
}
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;
@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
footprint += ' ';
}
power.AddNode(new MiniYamlNode("Footprint", footprint));
power.AddNode(new MiniYamlNodeBuilder("Footprint", footprint));
}
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();
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
var nukePowerTraits = actorNode.ChildrenMatching("NukePower");
foreach (var nukePowerTrait in nukePowerTraits)

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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})";

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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"))
{
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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)
{

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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RemoveNodes("ConditionManager");
yield break;

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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)
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" +
"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"))
{

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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");
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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var rdc in actorNode.ChildrenMatching("RenderDetectionCircle"))
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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var huca in actorNode.ChildrenMatching("HealUnitsCrateAction"))
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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var rp in actorNode.ChildrenMatching("InfiltrateForCash"))
rp.RenameChildrenMatching("Notification", "InfiltratedNotification");

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
"SelfHealing was renamed to ChangesHealth\n" +
"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"))
{

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" +
"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"))
{

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
("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 traitNode in actorNode.ChildrenMatching(traitName))
@@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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"))
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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
actorNode.RenameChildrenMatching("WithNukeLaunchAnimation", "WithSupportPowerActivationAnimation", 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 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"))
{
@@ -34,13 +34,13 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
var interval = burns.LastChildMatching("Interval");
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("Sequence", FieldSaver.FormatValue(animValue));
overlay.AddNode("IsDecoration", FieldSaver.FormatValue(true));
addNodes.Add(overlay);
var changesHealth = new MiniYamlNode("ChangesHealth", "");
var changesHealth = new MiniYamlNodeBuilder("ChangesHealth", "");
changesHealth.AddNode("Step", FieldSaver.FormatValue(-damageValue));
changesHealth.AddNode("StartIfBelow", FieldSaver.FormatValue(101));
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)
{
@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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"))
{

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 IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
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 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))
{

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)
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));
}
var removeNodes = new List<MiniYamlNode>();
var removeNodes = new List<MiniYamlNodeBuilder>();
foreach (var facing in actorNode.ChildrenMatching("TurretFacing"))
{
var turretFacing = WAngle.FromFacing(facing.NodeValue<int>()) - bodyFacing;
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
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"))
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 IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNode tilesetNode)
public override IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNodeBuilder tilesetNode)
{
if (tilesetNode.Key == "Templates")
{