Merge pull request #8000 from pchote/bogus-yaml-removals

Fix yaml merging
This commit is contained in:
Matthias Mailänder
2015-04-25 13:42:18 +02:00
8 changed files with 156 additions and 254 deletions

View File

@@ -16,15 +16,18 @@ using OpenRA.Traits;
namespace OpenRA
{
// TODO: This is not exported into the documentation yet.
[Desc("A unit/building inside the game. Every rules starts with one and adds trait to it.",
"Special actors like world or player are usually defined in system.yaml and affect everything.")]
/// <summary>
/// A unit/building inside the game. Every rules starts with one and adds trait to it.
/// Special actors like world or player are usually defined in system.yaml and affect everything.
/// </summary>
public class ActorInfo
{
[Desc("The actor name can be anything, but the sprites used in the Render*: traits default to this one.",
"If you add an ^ in front of the name, the engine will recognize this as a collection of traits",
"that can be inherited by others (using Inherits:) and not a real unit.",
"You can remove inherited traits by adding a - infront of them as in -TraitName: to inherit everything, but this trait.")]
/// <summary>
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
/// If you add an ^ in front of the name, the engine will recognize this as a collection of traits
/// that can be inherited by others (using Inherits:) and not a real unit.
/// You can remove inherited traits by adding a - infront of them as in -TraitName: to inherit everything, but this trait.
/// </summary>
public readonly string Name;
public readonly TypeDictionary Traits = new TypeDictionary();
List<ITraitInfo> constructOrderCache = null;
@@ -33,12 +36,22 @@ namespace OpenRA
{
try
{
var mergedNode = MergeWithParent(node, allUnits).ToDictionary();
var allParents = new HashSet<string>();
// Guard against circular inheritance
allParents.Add(name);
var mergedNode = MergeWithParents(node, allUnits, allParents).ToDictionary();
Name = name;
foreach (var t in mergedNode)
if (t.Key != "Inherits" && !t.Key.StartsWith("-"))
{
if (t.Key[0] == '-')
throw new YamlException("Bogus trait removal: " + t.Key);
if (t.Key != "Inherits" && !t.Key.StartsWith("Inherits@"))
Traits.Add(LoadTraitInfo(t.Key.Split('@')[0], t.Value));
}
}
catch (YamlException e)
{
@@ -46,32 +59,31 @@ namespace OpenRA
}
}
static MiniYaml GetParent(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
static Dictionary<string, MiniYaml> GetParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
{
MiniYaml inherits;
node.ToDictionary().TryGetValue("Inherits", out inherits);
if (inherits == null || string.IsNullOrEmpty(inherits.Value))
return null;
return node.Nodes.Where(n => n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
.ToDictionary(n => n.Value.Value, n =>
{
MiniYaml i;
if (!allUnits.TryGetValue(n.Value.Value, out i))
throw new YamlException(
"Bogus inheritance -- parent type {0} does not exist".F(n.Value.Value));
MiniYaml parent;
allUnits.TryGetValue(inherits.Value, out parent);
if (parent == null)
throw new InvalidOperationException(
"Bogus inheritance -- actor type {0} does not exist".F(inherits.Value));
return parent;
return i;
});
}
static MiniYaml MergeWithParent(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
static MiniYaml MergeWithParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits, HashSet<string> allParents)
{
var parent = GetParent(node, allUnits);
if (parent != null)
{
var result = MiniYaml.MergeStrict(node, MergeWithParent(parent, allUnits));
var parents = GetParents(node, allUnits);
// strip the '-'
result.Nodes.RemoveAll(a => a.Key.StartsWith("-"));
return result;
foreach (var kv in parents)
{
if (!allParents.Add(kv.Key))
throw new YamlException(
"Bogus inheritance -- duplicate inheritance of {0}.".F(kv.Key));
node = MiniYaml.MergeStrict(node, MergeWithParents(kv.Value, allUnits, allParents));
}
return node;

53
OpenRA.Game/MiniYaml.cs Executable file → Normal file
View File

@@ -254,17 +254,17 @@ namespace OpenRA
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries), fileName);
}
public static List<MiniYamlNode> MergeLiberal(List<MiniYamlNode> a, List<MiniYamlNode> b)
public static List<MiniYamlNode> MergeStrict(List<MiniYamlNode> a, List<MiniYamlNode> b)
{
return Merge(a, b, false);
}
public static List<MiniYamlNode> MergeStrict(List<MiniYamlNode> a, List<MiniYamlNode> b)
public static List<MiniYamlNode> MergeLiberal(List<MiniYamlNode> a, List<MiniYamlNode> b)
{
return Merge(a, b, true);
}
static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b, bool throwErrors)
static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b, bool allowUnresolvedRemoves = false)
{
if (a.Count == 0)
return b;
@@ -275,10 +275,11 @@ namespace OpenRA
var dictA = a.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
var dictB = b.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
var keys = dictA.Keys.Union(dictB.Keys).ToList();
var allKeys = dictA.Keys.Union(dictB.Keys);
var noInherit = keys.Where(x => x.Length > 0 && x[0] == '-')
.ToDictionary(x => x.Substring(1), x => false);
var keys = allKeys.Where(x => x.Length == 0 || x[0] != '-').ToList();
var removeKeys = allKeys.Where(x => x.Length > 0 && x[0] == '-')
.Select(k => k.Substring(1)).ToHashSet();
foreach (var key in keys)
{
@@ -286,47 +287,55 @@ namespace OpenRA
dictA.TryGetValue(key, out aa);
dictB.TryGetValue(key, out bb);
if (noInherit.ContainsKey(key))
{
if (!throwErrors)
if (aa != null)
ret.Add(aa);
noInherit[key] = true;
}
if (removeKeys.Contains(key))
removeKeys.Remove(key);
else
{
var loc = aa == null ? default(MiniYamlNode.SourceLocation) : aa.Location;
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, Merge(aa.Value, bb.Value, throwErrors), loc);
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, Merge(aa.Value, bb.Value, allowUnresolvedRemoves), loc);
ret.Add(merged);
}
}
if (throwErrors && noInherit.ContainsValue(false))
throw new YamlException("Bogus yaml removals: {0}".F(
noInherit.Where(x => !x.Value).JoinWith(", ")));
if (removeKeys.Any())
{
if (allowUnresolvedRemoves)
{
// Add the removal nodes back for the next pass to deal with
foreach (var k in removeKeys)
{
var key = "-" + k;
MiniYamlNode rem;
if (!dictA.TryGetValue(key, out rem))
rem = dictB[key];
ret.Add(rem);
}
}
else
throw new YamlException("Bogus yaml removals: {0}".F(removeKeys.JoinWith(", ")));
}
return ret;
}
public static MiniYaml MergeLiberal(MiniYaml a, MiniYaml b)
{
return Merge(a, b, false);
return Merge(a, b, true);
}
public static MiniYaml MergeStrict(MiniYaml a, MiniYaml b)
{
return Merge(a, b, true);
return Merge(a, b, false);
}
static MiniYaml Merge(MiniYaml a, MiniYaml b, bool throwErrors)
static MiniYaml Merge(MiniYaml a, MiniYaml b, bool allowUnresolvedRemoves)
{
if (a == null)
return b;
if (b == null)
return a;
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes, throwErrors));
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes, allowUnresolvedRemoves));
}
public IEnumerable<string> ToLines(string name)