diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 5d207956df..978220e072 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -1,4 +1,4 @@ - + Debug @@ -83,6 +83,7 @@ + @@ -277,7 +278,4 @@ --> - - - \ No newline at end of file diff --git a/OpenRA.Game/Traits/AttackBase.cs b/OpenRA.Game/Traits/AttackBase.cs index 7d2a5ada2e..4001cf4254 100644 --- a/OpenRA.Game/Traits/AttackBase.cs +++ b/OpenRA.Game/Traits/AttackBase.cs @@ -29,7 +29,9 @@ namespace OpenRA.Traits { public class AttackBaseInfo : ITraitInfo { + [WeaponReference] public readonly string PrimaryWeapon = null; + [WeaponReference] public readonly string SecondaryWeapon = null; public readonly int Recoil = 0; public readonly int[] PrimaryLocalOffset = { }; diff --git a/OpenRA.Game/Traits/LintAttributes.cs b/OpenRA.Game/Traits/LintAttributes.cs new file mode 100644 index 0000000000..b0aeac5a92 --- /dev/null +++ b/OpenRA.Game/Traits/LintAttributes.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRA.Traits +{ + /* attributes used by RALint to understand the rules */ + + [AttributeUsage(AttributeTargets.Field)] + public class ActorReferenceAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Field)] + public class WeaponReferenceAttribute : Attribute { } +} diff --git a/OpenRA.sln b/OpenRA.sln index 5bb2026243..f198a7e3bf 100644 --- a/OpenRA.sln +++ b/OpenRA.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Editor", "OpenRA.Edi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.TilesetBuilder", "OpenRA.TilesetBuilder\OpenRA.TilesetBuilder.csproj", "{56B1073B-AE14-499A-BB98-43A8DE9A39CA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RALint", "RALint\RALint.csproj", "{F9FA4D9F-2302-470A-8A07-6E37F488C124}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -108,6 +110,14 @@ Global {56B1073B-AE14-499A-BB98-43A8DE9A39CA}.Release|Any CPU.Build.0 = Release|Any CPU {56B1073B-AE14-499A-BB98-43A8DE9A39CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {56B1073B-AE14-499A-BB98-43A8DE9A39CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Release|Any CPU.Build.0 = Release|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F9FA4D9F-2302-470A-8A07-6E37F488C124}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/RALint/Program.cs b/RALint/Program.cs new file mode 100644 index 0000000000..2b3be11ad6 --- /dev/null +++ b/RALint/Program.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRA.FileFormats; +using OpenRA; +using OpenRA.GameRules; +using OpenRA.Traits; +using System.Reflection; + +namespace RALint +{ + static class Program + { + /* todo: move this into the engine? dpstool, seqed, editor, etc all need it (or something similar) */ + static void InitializeEngineWithMods(string[] mods) + { + AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly; + var manifest = new Manifest(mods); + Game.LoadModAssemblies(manifest); + + FileSystem.UnmountAll(); + foreach (var folder in manifest.Folders) FileSystem.Mount(folder); + foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg); + + Rules.LoadRules(manifest, new Map()); + } + + static int errors = 0; + + static void EmitError(string e) + { + Console.WriteLine(e); + ++errors; + } + + static int Main(string[] args) + { + InitializeEngineWithMods(args); + + foreach (var actorInfo in Rules.Info) + foreach (var traitInfo in actorInfo.Value.Traits.WithInterface()) + CheckTrait(actorInfo.Value, traitInfo); + + if (errors > 0) + { + Console.WriteLine("Errors: {0}", errors); + return 1; + } + + return 0; + } + + static void CheckTrait(ActorInfo actorInfo, ITraitInfo traitInfo) + { + var actualType = traitInfo.GetType(); + foreach (var field in actualType.GetFields()) + { + if (field.HasAttribute()) + CheckReference(actorInfo, traitInfo, field, Rules.Info, "actor"); + if (field.HasAttribute()) + CheckReference(actorInfo, traitInfo, field, Rules.Weapons, "weapon"); + } + } + + static string[] GetFieldValues(ITraitInfo traitInfo, FieldInfo fieldInfo) + { + var type = fieldInfo.FieldType; + if (type == typeof(string)) + return new string[] { (string)fieldInfo.GetValue(traitInfo) }; + if (type == typeof(string[])) + return (string[])fieldInfo.GetValue(traitInfo); + + EmitError("Bad type for reference on {0}.{1}. Supported types: string, string[]" + .F(traitInfo.GetType().Name, fieldInfo.Name)); + + return new string[] { }; + } + + static void CheckReference(ActorInfo actorInfo, ITraitInfo traitInfo, FieldInfo fieldInfo, + Dictionary dict, string type) + { + var values = GetFieldValues(traitInfo, fieldInfo); + foreach (var v in values) + if (v != null && !dict.ContainsKey(v.ToLowerInvariant())) + EmitError("{0}.{1}.{2}: Missing {3} `{4}`." + .F(actorInfo.Name, traitInfo.GetType().Name, fieldInfo.Name, type, v)); + } + + static bool HasAttribute(this MemberInfo mi) + { + return mi.GetCustomAttributes(typeof(T), true).Length != 0; + } + } +} diff --git a/RALint/Properties/AssemblyInfo.cs b/RALint/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..2713d0bd41 --- /dev/null +++ b/RALint/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RALint")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RALint")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("722ef098-e9f3-4e3c-b374-3388dfad8979")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RALint/RALint.csproj b/RALint/RALint.csproj new file mode 100644 index 0000000000..4cc79d0a91 --- /dev/null +++ b/RALint/RALint.csproj @@ -0,0 +1,71 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {F9FA4D9F-2302-470A-8A07-6E37F488C124} + Exe + Properties + RALint + RALint + v3.5 + 512 + + + true + full + false + ..\ + DEBUG;TRACE + prompt + 4 + x86 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + + + \ No newline at end of file