TechbloxModdingAPI/CodeGenerator/ECSClassGenerator.cs
Norbi Peti 9be1b5fdaf
Implemented improved ECS class generator based on entity descriptors
I can't test it yet because the API is full of errors because of the refactoring
2023-10-04 02:36:25 +02:00

129 lines
5.8 KiB
C#

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("<summary>", true);
private static readonly CodeCommentStatement _end = new("</summary>", true);
}
}