Added try/catch for TypeDictionary errors in Lint code
TypeDictionary errors are very hard for modders to debug as they don't mention which actor is causing the error
This commit is contained in:
@@ -33,24 +33,32 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
{
|
{
|
||||||
foreach (var actorInfo in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var count = actorInfo.Value.TraitInfos<IDefaultVisibilityInfo>().Count();
|
// Catch TypeDictionary errors
|
||||||
|
try
|
||||||
if (count == 0)
|
|
||||||
emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type!");
|
|
||||||
else if (count > 1)
|
|
||||||
emitError($"Actor type `{actorInfo.Key}` defines multiple default visibility types!");
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var vis = actorInfo.Value.TraitInfoOrDefault<HiddenUnderShroudInfo>();
|
var count = actorInfo.Value.TraitInfos<IDefaultVisibilityInfo>().Count();
|
||||||
if (vis != null && vis.Type == VisibilityType.Footprint)
|
|
||||||
|
if (count == 0)
|
||||||
|
emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type!");
|
||||||
|
else if (count > 1)
|
||||||
|
emitError($"Actor type `{actorInfo.Key}` defines multiple default visibility types!");
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var ios = actorInfo.Value.TraitInfoOrDefault<IOccupySpaceInfo>();
|
var vis = actorInfo.Value.TraitInfoOrDefault<HiddenUnderShroudInfo>();
|
||||||
if (ios == null)
|
if (vis != null && vis.Type == VisibilityType.Footprint)
|
||||||
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but has no IOccupySpace traits!");
|
{
|
||||||
else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0)
|
var ios = actorInfo.Value.TraitInfoOrDefault<IOccupySpaceInfo>();
|
||||||
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but does not have any footprint cells!");
|
if (ios == null)
|
||||||
|
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but has no IOccupySpace traits!");
|
||||||
|
else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0)
|
||||||
|
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{vis.GetType()}` but does not have any footprint cells!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,21 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
{
|
{
|
||||||
foreach (var actorInfo in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var health = actorInfo.Value.TraitInfoOrDefault<IHealthInfo>();
|
// Catch TypeDictionary errors
|
||||||
if (health == null)
|
try
|
||||||
continue;
|
{
|
||||||
|
var health = actorInfo.Value.TraitInfoOrDefault<IHealthInfo>();
|
||||||
|
if (health == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
var hitShapes = actorInfo.Value.TraitInfos<HitShapeInfo>();
|
var hitShapes = actorInfo.Value.TraitInfos<HitShapeInfo>();
|
||||||
if (!hitShapes.Any())
|
if (!hitShapes.Any())
|
||||||
emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait!");
|
emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait!");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,16 +32,24 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
{
|
{
|
||||||
foreach (var actorInfo in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var ios = actorInfo.Value.TraitInfoOrDefault<IOccupySpaceInfo>();
|
// Catch TypeDictionary errors
|
||||||
foreach (var rsi in actorInfo.Value.TraitInfos<RevealsShroudInfo>())
|
try
|
||||||
{
|
{
|
||||||
if (rsi.Type != VisibilityType.Footprint)
|
var ios = actorInfo.Value.TraitInfoOrDefault<IOccupySpaceInfo>();
|
||||||
continue;
|
foreach (var rsi in actorInfo.Value.TraitInfos<RevealsShroudInfo>())
|
||||||
|
{
|
||||||
|
if (rsi.Type != VisibilityType.Footprint)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (ios == null)
|
if (ios == null)
|
||||||
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but has no IOccupySpace traits!");
|
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but has no IOccupySpace traits!");
|
||||||
else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0)
|
else if (ios.OccupiedCells(actorInfo.Value, CPos.Zero).Count == 0)
|
||||||
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but does not have any footprint cells!");
|
emitError($"Actor type `{actorInfo.Key}` defines VisibilityType.Footprint in `{rsi.GetType()}` but does not have any footprint cells!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,68 +40,76 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
var factions = rules.Actors[SystemActors.World].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray();
|
var factions = rules.Actors[SystemActors.World].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray();
|
||||||
foreach (var actorInfo in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var images = new HashSet<string>();
|
// Catch TypeDictionary errors
|
||||||
|
try
|
||||||
// Actors may have 0 or 1 RenderSprites traits
|
|
||||||
var renderInfo = actorInfo.Value.TraitInfoOrDefault<RenderSpritesInfo>();
|
|
||||||
if (renderInfo != null)
|
|
||||||
{
|
{
|
||||||
images.Add(renderInfo.GetImage(actorInfo.Value, null).ToLowerInvariant());
|
var images = new HashSet<string>();
|
||||||
|
|
||||||
// Some actors define faction-specific artwork
|
// Actors may have 0 or 1 RenderSprites traits
|
||||||
foreach (var faction in factions)
|
var renderInfo = actorInfo.Value.TraitInfoOrDefault<RenderSpritesInfo>();
|
||||||
images.Add(renderInfo.GetImage(actorInfo.Value, faction).ToLowerInvariant());
|
if (renderInfo != null)
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
|
|
||||||
{
|
|
||||||
// Remove the "Info" suffix
|
|
||||||
var traitName = traitInfo.GetType().Name;
|
|
||||||
traitName = traitName.Remove(traitName.Length - 4);
|
|
||||||
|
|
||||||
var fields = traitInfo.GetType().GetFields();
|
|
||||||
foreach (var field in fields)
|
|
||||||
{
|
{
|
||||||
var sequenceReference = field.GetCustomAttributes<SequenceReferenceAttribute>(true).FirstOrDefault();
|
images.Add(renderInfo.GetImage(actorInfo.Value, null).ToLowerInvariant());
|
||||||
if (sequenceReference == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Some sequences may specify their own Image override
|
// Some actors define faction-specific artwork
|
||||||
IEnumerable<string> sequenceImages = images;
|
foreach (var faction in factions)
|
||||||
if (!string.IsNullOrEmpty(sequenceReference.ImageReference))
|
images.Add(renderInfo.GetImage(actorInfo.Value, faction).ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
|
||||||
|
{
|
||||||
|
// Remove the "Info" suffix
|
||||||
|
var traitName = traitInfo.GetType().Name;
|
||||||
|
traitName = traitName.Remove(traitName.Length - 4);
|
||||||
|
|
||||||
|
var fields = traitInfo.GetType().GetFields();
|
||||||
|
foreach (var field in fields)
|
||||||
{
|
{
|
||||||
var imageField = fields.First(f => f.Name == sequenceReference.ImageReference);
|
var sequenceReference = field.GetCustomAttributes<SequenceReferenceAttribute>(true).FirstOrDefault();
|
||||||
var imageOverride = (string)imageField.GetValue(traitInfo);
|
if (sequenceReference == null)
|
||||||
if (string.IsNullOrEmpty(imageOverride))
|
|
||||||
{
|
|
||||||
if (!sequenceReference.AllowNullImage)
|
|
||||||
emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` must define a value for `{sequenceReference.ImageReference}`");
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Some sequences may specify their own Image override
|
||||||
|
IEnumerable<string> sequenceImages = images;
|
||||||
|
if (!string.IsNullOrEmpty(sequenceReference.ImageReference))
|
||||||
|
{
|
||||||
|
var imageField = fields.First(f => f.Name == sequenceReference.ImageReference);
|
||||||
|
var imageOverride = (string)imageField.GetValue(traitInfo);
|
||||||
|
if (string.IsNullOrEmpty(imageOverride))
|
||||||
|
{
|
||||||
|
if (!sequenceReference.AllowNullImage)
|
||||||
|
emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` must define a value for `{sequenceReference.ImageReference}`");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceImages = new[] { imageOverride.ToLowerInvariant() };
|
||||||
}
|
}
|
||||||
|
|
||||||
sequenceImages = new[] { imageOverride.ToLowerInvariant() };
|
foreach (var sequence in LintExts.GetFieldValues(traitInfo, field, sequenceReference.DictionaryReference))
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var sequence in LintExts.GetFieldValues(traitInfo, field, sequenceReference.DictionaryReference))
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(sequence))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var i in sequenceImages)
|
|
||||||
{
|
{
|
||||||
if (sequenceReference.Prefix)
|
if (string.IsNullOrEmpty(sequence))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var i in sequenceImages)
|
||||||
{
|
{
|
||||||
// TODO: Remove prefixed sequence references and instead use explicit lists of lintable references
|
if (sequenceReference.Prefix)
|
||||||
if (!sequences.Sequences(i).Any(s => s.StartsWith(sequence)))
|
{
|
||||||
emitWarning($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` defines a prefix `{sequence}` that does not match any sequences on image `{i}`.");
|
// TODO: Remove prefixed sequence references and instead use explicit lists of lintable references
|
||||||
|
if (!sequences.Sequences(i).Any(s => s.StartsWith(sequence)))
|
||||||
|
emitWarning($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` defines a prefix `{sequence}` that does not match any sequences on image `{i}`.");
|
||||||
|
}
|
||||||
|
else if (!sequences.HasSequence(i, sequence))
|
||||||
|
emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` references an undefined sequence `{sequence}` on image `{i}`.");
|
||||||
}
|
}
|
||||||
else if (!sequences.HasSequence(i, sequence))
|
|
||||||
emitError($"Actor type `{actorInfo.Value.Name}` trait `{traitName}` field `{field.Name}` references an undefined sequence `{sequence}` on image `{i}`.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var weaponInfo in rules.Weapons)
|
foreach (var weaponInfo in rules.Weapons)
|
||||||
|
|||||||
@@ -32,13 +32,21 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
{
|
{
|
||||||
foreach (var actorInfo in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var buildable = actorInfo.Value.TraitInfoOrDefault<BuildableInfo>();
|
// Catch TypeDictionary errors
|
||||||
if (buildable == null)
|
try
|
||||||
continue;
|
{
|
||||||
|
var buildable = actorInfo.Value.TraitInfoOrDefault<BuildableInfo>();
|
||||||
|
if (buildable == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
var tooltip = actorInfo.Value.TraitInfos<TooltipInfo>().FirstOrDefault(info => info.EnabledByDefault);
|
var tooltip = actorInfo.Value.TraitInfos<TooltipInfo>().FirstOrDefault(info => info.EnabledByDefault);
|
||||||
if (tooltip == null)
|
if (tooltip == null)
|
||||||
emitError("The following buildable actor has no (enabled) Tooltip: " + actorInfo.Key);
|
emitError("The following buildable actor has no (enabled) Tooltip: " + actorInfo.Key);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,14 +33,21 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos<ITechTreePrerequisiteInfo>().SelectMany(p => p.Prerequisites(a.Value)));
|
var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos<ITechTreePrerequisiteInfo>().SelectMany(p => p.Prerequisites(a.Value)));
|
||||||
|
|
||||||
// TODO: this check is case insensitive while the real check in-game is not
|
// TODO: this check is case insensitive while the real check in-game is not
|
||||||
foreach (var i in rules.Actors)
|
foreach (var actorInfo in rules.Actors)
|
||||||
{
|
{
|
||||||
var bi = i.Value.TraitInfoOrDefault<BuildableInfo>();
|
// Catch TypeDictionary errors
|
||||||
if (bi != null)
|
try
|
||||||
foreach (var prereq in bi.Prerequisites)
|
{
|
||||||
if (!prereq.StartsWith("~disabled"))
|
var bi = actorInfo.Value.TraitInfoOrDefault<BuildableInfo>();
|
||||||
if (!providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", "")))
|
if (bi != null)
|
||||||
emitError($"Buildable actor {i.Key} has prereq {prereq} not provided by anything.");
|
foreach (var prereq in bi.Prerequisites)
|
||||||
|
if (!prereq.StartsWith("~disabled") && !providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", "")))
|
||||||
|
emitError($"Buildable actor {actorInfo.Key} has prereq {prereq} not provided by anything.");
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
|
{
|
||||||
|
emitError($"{e.Message} (Actor type `{actorInfo.Key}`)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user