using System; using System.CodeDom; using System.CodeDom.Compiler; using System.IO; using System.Linq; namespace CodeGenerator { public static class ECSClassGenerator { public static void Generate(Type entityDescriptorType) { var info = ECSAnalyzer.AnalyzeEntityDescriptor(entityDescriptorType); var codeUnit = new CodeCompileUnit(); var ns = new CodeNamespace("TechbloxModdingAPI.Blocks"); ns.Imports.Add(new CodeNamespaceImport("RobocraftX.Common")); ns.Imports.Add(new CodeNamespaceImport("Svelto.ECS")); var cl = new CodeTypeDeclaration(info.Name); cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock")); cl.Members.Add(new CodeConstructor { Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")}, Comments = { _start, new CodeCommentStatement($"Constructs a(n) {info.Name} object representing an existing block.", true), _end }, BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")}, Attributes = MemberAttributes.Public | MemberAttributes.Final }); foreach (var propertyInfo in info.Properties) { if (propertyInfo is ECSReflectedPropertyInfo reflectedPropertyInfo) GenerateReflectedProperty(reflectedPropertyInfo, cl); else GenerateProperty(propertyInfo, cl); } ns.Types.Add(cl); codeUnit.Namespaces.Add(ns); var provider = CodeDomProvider.CreateProvider("CSharp"); var path = $"../../../../TechbloxModdingAPI/Blocks/{info.Name}.cs"; using (var sw = new StreamWriter(path)) { provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"}); } File.WriteAllLines(path, File.ReadAllLines(path).SkipWhile(line => line.StartsWith("//") || line.Length == 0)); } private static void GenerateProperty(ECSPropertyInfo info, CodeTypeDeclaration cl) { var getStruct = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"), "GetBlockInfo", new CodeTypeReference(info.ComponentType)), new CodeThisReferenceExpression()); CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, info.Name); cl.Members.Add(new CodeMemberProperty { Name = info.Name, HasGet = true, HasSet = true, GetStatements = { new CodeMethodReturnStatement(structFieldReference) }, SetStatements = { new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression()) }, Type = new CodeTypeReference(info.Type), Attributes = MemberAttributes.Public | MemberAttributes.Final, Comments = { _start, new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." + " May not be saved.", // TODO: Doesn't know if tweakable stat true), _end } }); } private static void GenerateReflectedProperty(ECSReflectedPropertyInfo info, CodeTypeDeclaration cl) { var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{info.OriginalClassName}\")"); CodeExpression reflectedGet = new CodeCastExpression(info.Type, new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"), "GetBlockInfo"), new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name))); CodeExpression reflectedSet = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"), "SetBlockInfo"), new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name), new CodePropertySetValueReferenceExpression()); cl.Members.Add(new CodeMemberProperty { Name = info.Name, HasGet = true, HasSet = true, GetStatements = { new CodeMethodReturnStatement(reflectedGet) }, SetStatements = { (CodeStatement)new CodeExpressionStatement(reflectedSet) }, Type = new CodeTypeReference(info.Type), Attributes = MemberAttributes.Public | MemberAttributes.Final, Comments = { _start, new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." + " May not be saved.", // TODO: Doesn't know if tweakable stat true), _end } }); } private static readonly CodeCommentStatement _start = new("", true); private static readonly CodeCommentStatement _end = new("", true); } }