1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System; 13 using System.Linq; 14 using System.Reflection; 15 using OpenRA.GameRules; 16 using OpenRA.Traits; 17 18 namespace OpenRA.Mods.Common.Lint 19 { 20 public class CheckActorReferences : ILintRulesPass 21 { 22 Action<string> emitError; 23 Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)24 public void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules) 25 { 26 this.emitError = emitError; 27 28 foreach (var actorInfo in rules.Actors) 29 foreach (var traitInfo in actorInfo.Value.TraitInfos<ITraitInfo>()) 30 CheckTrait(actorInfo.Value, traitInfo, rules); 31 } 32 CheckTrait(ActorInfo actorInfo, ITraitInfo traitInfo, Ruleset rules)33 void CheckTrait(ActorInfo actorInfo, ITraitInfo traitInfo, Ruleset rules) 34 { 35 var actualType = traitInfo.GetType(); 36 foreach (var field in actualType.GetFields()) 37 { 38 if (field.HasAttribute<ActorReferenceAttribute>()) 39 CheckActorReference(actorInfo, traitInfo, field, rules.Actors, 40 field.GetCustomAttributes<ActorReferenceAttribute>(true)[0]); 41 42 if (field.HasAttribute<WeaponReferenceAttribute>()) 43 CheckWeaponReference(actorInfo, traitInfo, field, rules.Weapons, 44 field.GetCustomAttributes<WeaponReferenceAttribute>(true)[0]); 45 46 if (field.HasAttribute<VoiceSetReferenceAttribute>()) 47 CheckVoiceReference(actorInfo, traitInfo, field, rules.Voices, 48 field.GetCustomAttributes<VoiceSetReferenceAttribute>(true)[0]); 49 } 50 } 51 CheckActorReference(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, ActorInfo> dict, ActorReferenceAttribute attribute)52 void CheckActorReference(ActorInfo actorInfo, 53 ITraitInfo traitInfo, 54 FieldInfo fieldInfo, 55 IReadOnlyDictionary<string, ActorInfo> dict, 56 ActorReferenceAttribute attribute) 57 { 58 var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); 59 foreach (var value in values) 60 { 61 if (value == null) 62 continue; 63 64 // NOTE: Once https://github.com/OpenRA/OpenRA/issues/4124 is resolved we won't 65 // have to .ToLower* anything here. 66 var v = value.ToLowerInvariant(); 67 68 if (!dict.ContainsKey(v)) 69 { 70 emitError("{0}.{1}.{2}: Missing actor `{3}`." 71 .F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, value)); 72 73 continue; 74 } 75 76 foreach (var requiredTrait in attribute.RequiredTraits) 77 if (!dict[v].TraitsInConstructOrder().Any(t => t.GetType() == requiredTrait || t.GetType().IsSubclassOf(requiredTrait))) 78 emitError("Actor type {0} does not have trait {1} which is required by {2}.{3}." 79 .F(value, requiredTrait.Name, traitInfo.GetType().Name, fieldInfo.Name)); 80 } 81 } 82 CheckWeaponReference(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, WeaponInfo> dict, WeaponReferenceAttribute attribute)83 void CheckWeaponReference(ActorInfo actorInfo, 84 ITraitInfo traitInfo, 85 FieldInfo fieldInfo, 86 IReadOnlyDictionary<string, WeaponInfo> dict, 87 WeaponReferenceAttribute attribute) 88 { 89 var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); 90 foreach (var value in values) 91 { 92 if (value == null) 93 continue; 94 95 if (!dict.ContainsKey(value.ToLower())) 96 emitError("{0}.{1}.{2}: Missing weapon `{3}`." 97 .F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, value)); 98 } 99 } 100 CheckVoiceReference(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, SoundInfo> dict, VoiceSetReferenceAttribute attribute)101 void CheckVoiceReference(ActorInfo actorInfo, 102 ITraitInfo traitInfo, 103 FieldInfo fieldInfo, 104 IReadOnlyDictionary<string, SoundInfo> dict, 105 VoiceSetReferenceAttribute attribute) 106 { 107 var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); 108 foreach (var value in values) 109 { 110 if (value == null) 111 continue; 112 113 if (!dict.ContainsKey(value.ToLower())) 114 emitError("{0}.{1}.{2}: Missing voice `{3}`." 115 .F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, value)); 116 } 117 } 118 } 119 } 120