A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
7.1KB

  1. using System;
  2. using System.CodeDom;
  3. using System.CodeDom.Compiler;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using Gamecraft.Tweaks;
  9. using RobocraftX.Common;
  10. using Svelto.ECS;
  11. namespace CodeGenerator
  12. {
  13. public class BlockClassGenerator
  14. {
  15. public void Generate(string name, string group = null, Dictionary<string, string> renames = null, params Type[] types)
  16. {
  17. if (group is null)
  18. {
  19. group = GetGroup(name) + "_BLOCK_GROUP";
  20. if (typeof(CommonExclusiveGroups).GetFields().All(field => field.Name != group))
  21. group = GetGroup(name) + "_BLOCK_BUILD_GROUP";
  22. }
  23. if (!group.Contains('.'))
  24. group = "CommonExclusiveGroups." + group;
  25. var codeUnit = new CodeCompileUnit();
  26. var ns = new CodeNamespace("TechbloxModdingAPI.Blocks");
  27. ns.Imports.Add(new CodeNamespaceImport("RobocraftX.Common"));
  28. ns.Imports.Add(new CodeNamespaceImport("Svelto.ECS"));
  29. var cl = new CodeTypeDeclaration(name);
  30. //cl.BaseTypes.Add(baseClass != null ? new CodeTypeReference(baseClass) : new CodeTypeReference("Block"));
  31. cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
  32. cl.Members.Add(new CodeConstructor
  33. {
  34. Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
  35. Comments =
  36. {
  37. _start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
  38. },
  39. BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
  40. Attributes = MemberAttributes.Public | MemberAttributes.Final
  41. });
  42. cl.Members.Add(new CodeConstructor
  43. {
  44. Parameters =
  45. {
  46. new CodeParameterDeclarationExpression(typeof(uint), "id")
  47. },
  48. Comments =
  49. {
  50. _start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
  51. },
  52. BaseConstructorArgs =
  53. {
  54. new CodeObjectCreateExpression("EGID", new CodeVariableReferenceExpression("id"),
  55. new CodeVariableReferenceExpression(group))
  56. },
  57. Attributes = MemberAttributes.Public | MemberAttributes.Final
  58. });
  59. foreach (var type in types)
  60. {
  61. GenerateProperties(cl, type, name, renames);
  62. }
  63. ns.Types.Add(cl);
  64. codeUnit.Namespaces.Add(ns);
  65. var provider = CodeDomProvider.CreateProvider("CSharp");
  66. var path = $@"../../../../TechbloxModdingAPI/Blocks/{name}.cs";
  67. using (var sw = new StreamWriter(path))
  68. {
  69. provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"});
  70. }
  71. File.WriteAllLines(path,
  72. File.ReadAllLines(path).SkipWhile(line => line.StartsWith("//") || line.Length == 0));
  73. }
  74. private static string GetGroup(string name)
  75. {
  76. var ret = "";
  77. foreach (var ch in name)
  78. {
  79. if (char.IsUpper(ch) && ret.Length > 0)
  80. ret += "_" + ch;
  81. else
  82. ret += char.ToUpper(ch);
  83. }
  84. return ret;
  85. }
  86. private void GenerateProperties(CodeTypeDeclaration cl, Type type, string baseClass,
  87. Dictionary<string, string> renames)
  88. {
  89. if (!typeof(IEntityComponent).IsAssignableFrom(type))
  90. throw new ArgumentException("Type must be an entity component");
  91. bool reflection = type.IsNotPublic;
  92. var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{type.FullName}\")");
  93. foreach (var field in type.GetFields())
  94. {
  95. var attr = field.GetCustomAttribute<TweakableStatAttribute>();
  96. if (renames == null || !renames.TryGetValue(field.Name, out var propName))
  97. {
  98. propName = field.Name;
  99. if (attr != null)
  100. propName = attr.propertyName;
  101. }
  102. propName = char.ToUpper(propName[0]) + propName.Substring(1);
  103. var getStruct = new CodeMethodInvokeExpression(
  104. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  105. "GetBlockInfo", new CodeTypeReference(type)),
  106. new CodeThisReferenceExpression());
  107. CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, field.Name);
  108. CodeExpression reflectedGet = new CodeCastExpression(field.FieldType, new CodeMethodInvokeExpression(
  109. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  110. "GetBlockInfo"),
  111. new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name)));
  112. CodeExpression reflectedSet = new CodeMethodInvokeExpression(
  113. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  114. "SetBlockInfo"),
  115. new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name),
  116. new CodePropertySetValueReferenceExpression());
  117. cl.Members.Add(new CodeMemberProperty
  118. {
  119. Name = propName,
  120. HasGet = true,
  121. HasSet = true,
  122. GetStatements =
  123. {
  124. new CodeMethodReturnStatement(reflection ? reflectedGet : structFieldReference)
  125. },
  126. SetStatements =
  127. {
  128. reflection
  129. ? (CodeStatement)new CodeExpressionStatement(reflectedSet)
  130. : new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
  131. },
  132. Type = new CodeTypeReference(field.FieldType),
  133. Attributes = MemberAttributes.Public | MemberAttributes.Final,
  134. Comments =
  135. {
  136. _start,
  137. new CodeCommentStatement($"Gets or sets the {baseClass}'s {propName} property." +
  138. $" {(attr != null ? "Tweakable stat." : "May not be saved.")}",
  139. true),
  140. _end
  141. }
  142. });
  143. }
  144. }
  145. private static readonly CodeCommentStatement _start = new CodeCommentStatement("<summary>", true);
  146. private static readonly CodeCommentStatement _end = new CodeCommentStatement("</summary>", true);
  147. }
  148. }