|
- 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;
-
- 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");
- bool reflection = type.IsNotPublic;
- var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{type.FullName}\")");
- 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 getStruct = new CodeMethodInvokeExpression(
- new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
- "GetBlockInfo", new CodeTypeReference(type)),
- new CodeThisReferenceExpression());
- CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, field.Name);
- CodeExpression reflectedGet = new CodeCastExpression(field.FieldType, new CodeMethodInvokeExpression(
- new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
- "GetBlockInfo"),
- new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name)));
- CodeExpression reflectedSet = new CodeMethodInvokeExpression(
- new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
- "SetBlockInfo"),
- new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name),
- new CodePropertySetValueReferenceExpression());
- cl.Members.Add(new CodeMemberProperty
- {
- Name = propName,
- HasGet = true,
- HasSet = true,
- GetStatements =
- {
- new CodeMethodReturnStatement(reflection ? reflectedGet : structFieldReference)
- },
- SetStatements =
- {
- reflection
- ? (CodeStatement)new CodeExpressionStatement(reflectedSet)
- : 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);
- }
- }
|