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.

142 lines
5.9KB

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