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
This commit is contained in:
parent
9a195215f9
commit
9be1b5fdaf
7 changed files with 226 additions and 17 deletions
65
CodeGenerator/ECSAnalyzer.cs
Normal file
65
CodeGenerator/ECSAnalyzer.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.CodeDom;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Tweaks;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
public class ECSAnalyzer
|
||||
{
|
||||
public static ECSClassInfo AnalyzeEntityDescriptor(Type entityDescriptorType)
|
||||
{
|
||||
// TODO: Add support for creating/deleting entities (getting an up to date server/client engines root)
|
||||
var templateType = typeof(EntityDescriptorTemplate<>).MakeGenericType(entityDescriptorType);
|
||||
var getTemplateClass = Expression.Constant(templateType);
|
||||
var getDescriptorExpr = Expression.PropertyOrField(getTemplateClass, "descriptor");
|
||||
var getTemplateDescriptorExpr =
|
||||
Expression.Lambda<Func<IEntityDescriptor>>(getDescriptorExpr);
|
||||
var getTemplateDescriptor = getTemplateDescriptorExpr.Compile();
|
||||
var builders = getTemplateDescriptor().componentsToBuild;
|
||||
return new ECSClassInfo
|
||||
{
|
||||
Name = entityDescriptorType.Name.Replace("EntityComponent", "").Replace("EntityStruct", ""),
|
||||
Properties = builders.Select(builder => builder.GetEntityComponentType()).SelectMany(AnalyzeFields).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static ECSPropertyInfo[] AnalyzeFields(Type componentType)
|
||||
{
|
||||
bool useReflection = componentType.IsNotPublic;
|
||||
var result = new List<ECSPropertyInfo>();
|
||||
foreach (var field in componentType.GetFields())
|
||||
{
|
||||
var attr = field.GetCustomAttribute<TweakableStatAttribute>();
|
||||
string propName = field.Name;
|
||||
if (attr != null)
|
||||
propName = attr.propertyName;
|
||||
|
||||
propName = char.ToUpper(propName[0]) + propName[1..];
|
||||
if (useReflection)
|
||||
{
|
||||
result.Add(new ECSReflectedPropertyInfo
|
||||
{
|
||||
Name = propName,
|
||||
Type = field.FieldType,
|
||||
OriginalClassName = componentType.FullName,
|
||||
ComponentType = componentType
|
||||
});
|
||||
}
|
||||
|
||||
result.Add(new ECSPropertyInfo
|
||||
{
|
||||
Name = propName,
|
||||
Type = field.FieldType,
|
||||
ComponentType = componentType
|
||||
});
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
129
CodeGenerator/ECSClassGenerator.cs
Normal file
129
CodeGenerator/ECSClassGenerator.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
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);
|
||||
}
|
||||
}
|
10
CodeGenerator/ECSClassInfo.cs
Normal file
10
CodeGenerator/ECSClassInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
public class ECSClassInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ECSPropertyInfo[] Properties { get; set; }
|
||||
}
|
||||
}
|
11
CodeGenerator/ECSPropertyInfo.cs
Normal file
11
CodeGenerator/ECSPropertyInfo.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
public class ECSPropertyInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Type Type { get; set; }
|
||||
public Type ComponentType { get; set; }
|
||||
}
|
||||
}
|
7
CodeGenerator/ECSReflectedPropertyInfo.cs
Normal file
7
CodeGenerator/ECSReflectedPropertyInfo.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace CodeGenerator
|
||||
{
|
||||
public class ECSReflectedPropertyInfo : ECSPropertyInfo
|
||||
{
|
||||
public string OriginalClassName { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
using Svelto.ECS;
|
||||
using Techblox.Destruction;
|
||||
using Techblox.TimeRunning.Clusters;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
|
||||
/// Represents a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
|
||||
/// Only exists if a cluster destruction manager is present. Static blocks like grass and dirt aren't part of a cluster.
|
||||
/// </summary>
|
||||
public class Cluster : EcsObjectBase
|
||||
{
|
||||
public Cluster(EGID id) : base(id)
|
||||
{
|
||||
public Cluster(EGID id) : base(id, typeof(ResetDestructionUtility))
|
||||
{ // TODO: Damage has been connection-based for a while
|
||||
}
|
||||
|
||||
public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||
|
|
|
@ -48,20 +48,6 @@ namespace TechbloxModdingAPI
|
|||
Id = id;
|
||||
}
|
||||
|
||||
private void AnalyzeEntityDescriptor(Type entityDescriptorType)
|
||||
{
|
||||
// TODO: Cache
|
||||
// TODO: This should be in BlockClassGenerator
|
||||
// TODO: Add support for creating/deleting entities (getting an up to date server/client engines root)
|
||||
var templateType = typeof(EntityDescriptorTemplate<>).MakeGenericType(entityDescriptorType);
|
||||
var getTemplateClass = Expression.Constant(templateType);
|
||||
var getDescriptorExpr = Expression.PropertyOrField(getTemplateClass, "descriptor");
|
||||
var getTemplateDescriptorExpr =
|
||||
Expression.Lambda<Func<IEntityDescriptor>>(getDescriptorExpr);
|
||||
var getTemplateDescriptor = getTemplateDescriptorExpr.Compile();
|
||||
var builders = getTemplateDescriptor().componentsToBuild;
|
||||
}
|
||||
|
||||
#region ECS initializer stuff
|
||||
|
||||
protected internal EcsInitData InitData;
|
||||
|
|
Loading…
Reference in a new issue