TechbloxModdingAPI/CodeGenerator/BlockClassGenerator.cs
Norbi Peti c0eae77421 Finish code generator (mostly)
Made the generated members public and final
Removed the comment from the start of the files
Generating the files directly into the project folder
Added comments describing the generated constructors and properties
Using SignalingBlock as a base class
Added support for renaming properties
Added support for getting the name from the tweakable stat name
Added/updated functional block classes
2021-08-11 23:44:26 +02:00

142 lines
5.9 KiB
C#

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Gamecraft.Tweaks;
using RobocraftX.Common;
using Svelto.ECS;
using Techblox.EngineBlock;
namespace CodeGenerator
{
public class BlockClassGenerator
{
public void Generate(string name, string group = null, Dictionary<string, string> renames = null, params Type[] types)
{
if (group is null)
{
group = GetGroup(name) + "_BLOCK_GROUP";
if (typeof(CommonExclusiveGroups).GetFields().All(field => field.Name != group))
group = GetGroup(name) + "_BLOCK_BUILD_GROUP";
}
if (!group.Contains("."))
group = "CommonExclusiveGroups." + group;
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(name);
//cl.BaseTypes.Add(baseClass != null ? new CodeTypeReference(baseClass) : new CodeTypeReference("Block"));
cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
cl.Members.Add(new CodeConstructor
{
Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
Comments =
{
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
},
BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
Attributes = MemberAttributes.Public | MemberAttributes.Final
});
cl.Members.Add(new CodeConstructor
{
Parameters =
{
new CodeParameterDeclarationExpression(typeof(uint), "id")
},
Comments =
{
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
},
BaseConstructorArgs =
{
new CodeObjectCreateExpression("EGID", new CodeVariableReferenceExpression("id"),
new CodeVariableReferenceExpression(group))
},
Attributes = MemberAttributes.Public | MemberAttributes.Final
});
foreach (var type in types)
{
GenerateProperties(cl, type, name, renames);
}
ns.Types.Add(cl);
codeUnit.Namespaces.Add(ns);
var provider = CodeDomProvider.CreateProvider("CSharp");
var path = $@"..\..\..\TechbloxModdingAPI\Blocks\{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 string GetGroup(string name)
{
var ret = "";
foreach (var ch in name)
{
if (char.IsUpper(ch) && ret.Length > 0)
ret += "_" + ch;
else
ret += char.ToUpper(ch);
}
return ret;
}
private void GenerateProperties(CodeTypeDeclaration cl, Type type, string baseClass,
Dictionary<string, string> renames)
{
if (!typeof(IEntityComponent).IsAssignableFrom(type))
throw new ArgumentException("Type must be an entity component");
foreach (var field in type.GetFields())
{
var attr = field.GetCustomAttribute<TweakableStatAttribute>();
if (renames == null || !renames.TryGetValue(field.Name, out var propName))
{
propName = field.Name;
if (attr != null)
propName = attr.propertyName;
}
propName = char.ToUpper(propName[0]) + propName.Substring(1);
var structFieldReference = new CodeFieldReferenceExpression(new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
"GetBlockInfo", new CodeTypeReference(type)),
new CodeThisReferenceExpression()), field.Name);
cl.Members.Add(new CodeMemberProperty
{
Name = propName,
HasGet = true,
HasSet = true,
GetStatements =
{
new CodeMethodReturnStatement(structFieldReference)
},
SetStatements =
{
new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
},
Type = new CodeTypeReference(field.FieldType),
Attributes = MemberAttributes.Public | MemberAttributes.Final,
Comments =
{
_start, new CodeCommentStatement($"Gets or sets the {baseClass}'s {propName} property." +
$" {(attr != null ? "Tweakable stat." : "May not be saved.")}", true), _end
}
});
}
}
private static readonly CodeCommentStatement _start = new CodeCommentStatement("<summary>", true);
private static readonly CodeCommentStatement _end = new CodeCommentStatement("</summary>", true);
}
}