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.

129 lines
5.8KB

  1. using System;
  2. using System.CodeDom;
  3. using System.CodeDom.Compiler;
  4. using System.IO;
  5. using System.Linq;
  6. namespace CodeGenerator
  7. {
  8. public static class ECSClassGenerator
  9. {
  10. public static void Generate(Type entityDescriptorType)
  11. {
  12. var info = ECSAnalyzer.AnalyzeEntityDescriptor(entityDescriptorType);
  13. var codeUnit = new CodeCompileUnit();
  14. var ns = new CodeNamespace("TechbloxModdingAPI.Blocks");
  15. ns.Imports.Add(new CodeNamespaceImport("RobocraftX.Common"));
  16. ns.Imports.Add(new CodeNamespaceImport("Svelto.ECS"));
  17. var cl = new CodeTypeDeclaration(info.Name);
  18. cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
  19. cl.Members.Add(new CodeConstructor
  20. {
  21. Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
  22. Comments =
  23. {
  24. _start,
  25. new CodeCommentStatement($"Constructs a(n) {info.Name} object representing an existing block.", true),
  26. _end
  27. },
  28. BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
  29. Attributes = MemberAttributes.Public | MemberAttributes.Final
  30. });
  31. foreach (var propertyInfo in info.Properties)
  32. {
  33. if (propertyInfo is ECSReflectedPropertyInfo reflectedPropertyInfo)
  34. GenerateReflectedProperty(reflectedPropertyInfo, cl);
  35. else
  36. GenerateProperty(propertyInfo, cl);
  37. }
  38. ns.Types.Add(cl);
  39. codeUnit.Namespaces.Add(ns);
  40. var provider = CodeDomProvider.CreateProvider("CSharp");
  41. var path = $"../../../../TechbloxModdingAPI/Blocks/{info.Name}.cs";
  42. using (var sw = new StreamWriter(path))
  43. {
  44. provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"});
  45. }
  46. File.WriteAllLines(path,
  47. File.ReadAllLines(path).SkipWhile(line => line.StartsWith("//") || line.Length == 0));
  48. }
  49. private static void GenerateProperty(ECSPropertyInfo info, CodeTypeDeclaration cl)
  50. {
  51. var getStruct = new CodeMethodInvokeExpression(
  52. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  53. "GetBlockInfo", new CodeTypeReference(info.ComponentType)),
  54. new CodeThisReferenceExpression());
  55. CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, info.Name);
  56. cl.Members.Add(new CodeMemberProperty
  57. {
  58. Name = info.Name,
  59. HasGet = true,
  60. HasSet = true,
  61. GetStatements =
  62. {
  63. new CodeMethodReturnStatement(structFieldReference)
  64. },
  65. SetStatements =
  66. {
  67. new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
  68. },
  69. Type = new CodeTypeReference(info.Type),
  70. Attributes = MemberAttributes.Public | MemberAttributes.Final,
  71. Comments =
  72. {
  73. _start,
  74. new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." +
  75. " May not be saved.", // TODO: Doesn't know if tweakable stat
  76. true),
  77. _end
  78. }
  79. });
  80. }
  81. private static void GenerateReflectedProperty(ECSReflectedPropertyInfo info, CodeTypeDeclaration cl)
  82. {
  83. var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{info.OriginalClassName}\")");
  84. CodeExpression reflectedGet = new CodeCastExpression(info.Type, new CodeMethodInvokeExpression(
  85. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  86. "GetBlockInfo"),
  87. new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name)));
  88. CodeExpression reflectedSet = new CodeMethodInvokeExpression(
  89. new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
  90. "SetBlockInfo"),
  91. new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(info.Name),
  92. new CodePropertySetValueReferenceExpression());
  93. cl.Members.Add(new CodeMemberProperty
  94. {
  95. Name = info.Name,
  96. HasGet = true,
  97. HasSet = true,
  98. GetStatements =
  99. {
  100. new CodeMethodReturnStatement(reflectedGet)
  101. },
  102. SetStatements =
  103. {
  104. (CodeStatement)new CodeExpressionStatement(reflectedSet)
  105. },
  106. Type = new CodeTypeReference(info.Type),
  107. Attributes = MemberAttributes.Public | MemberAttributes.Final,
  108. Comments =
  109. {
  110. _start,
  111. new CodeCommentStatement($"Gets or sets the {info.ComponentType.Name}'s {info.Name} property." +
  112. " May not be saved.", // TODO: Doesn't know if tweakable stat
  113. true),
  114. _end
  115. }
  116. });
  117. }
  118. private static readonly CodeCommentStatement _start = new("<summary>", true);
  119. private static readonly CodeCommentStatement _end = new("</summary>", true);
  120. }
  121. }