ultimate-unreal-engine-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +729 -0
  2. package/dist/build/error-parser.js +51 -0
  3. package/dist/build/fix-suggester.js +84 -0
  4. package/dist/build/ubt-runner.js +146 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/config.js +8 -0
  7. package/dist/docs/data/ue57-api.js +228 -0
  8. package/dist/docs/doc-index.js +110 -0
  9. package/dist/docs/types.js +4 -0
  10. package/dist/generators/class-generator.js +363 -0
  11. package/dist/generators/file-modifier.js +276 -0
  12. package/dist/generators/uht-validator.js +177 -0
  13. package/dist/index.js +89 -0
  14. package/dist/parsers/cpp-class-index.js +230 -0
  15. package/dist/parsers/cpp-parser.js +369 -0
  16. package/dist/parsers/ini-parser.js +216 -0
  17. package/dist/parsers/uproject-parser.js +130 -0
  18. package/dist/plugin-bridge/client.js +217 -0
  19. package/dist/plugin-bridge/protocol.js +6 -0
  20. package/dist/plugin-bridge/retry.js +23 -0
  21. package/dist/setup.js +209 -0
  22. package/dist/tools/ai-systems/index.js +247 -0
  23. package/dist/tools/ai-systems/types.js +4 -0
  24. package/dist/tools/animation/index.js +241 -0
  25. package/dist/tools/animation/types.js +4 -0
  26. package/dist/tools/audio/index.js +204 -0
  27. package/dist/tools/audio/types.js +4 -0
  28. package/dist/tools/blueprint/index.js +495 -0
  29. package/dist/tools/blueprint/types.js +4 -0
  30. package/dist/tools/build/index.js +163 -0
  31. package/dist/tools/chaos/index.js +230 -0
  32. package/dist/tools/chaos/types.js +4 -0
  33. package/dist/tools/collision-physics/index.js +211 -0
  34. package/dist/tools/config/index.js +288 -0
  35. package/dist/tools/cpp/index.js +305 -0
  36. package/dist/tools/docs/index.js +251 -0
  37. package/dist/tools/editor/index.js +242 -0
  38. package/dist/tools/gas/index.js +222 -0
  39. package/dist/tools/gas/types.js +5 -0
  40. package/dist/tools/import-export/index.js +218 -0
  41. package/dist/tools/input/index.js +146 -0
  42. package/dist/tools/known-issues/index.js +88 -0
  43. package/dist/tools/known-issues/middleware.js +55 -0
  44. package/dist/tools/known-issues/store.js +125 -0
  45. package/dist/tools/livelink/index.js +203 -0
  46. package/dist/tools/livelink/types.js +4 -0
  47. package/dist/tools/material/index.js +190 -0
  48. package/dist/tools/motion-design/index.js +251 -0
  49. package/dist/tools/motion-design/types.js +6 -0
  50. package/dist/tools/movie-render/index.js +220 -0
  51. package/dist/tools/networking/index.js +149 -0
  52. package/dist/tools/pcg/index.js +164 -0
  53. package/dist/tools/selection/index.js +180 -0
  54. package/dist/tools/sequencer/index.js +218 -0
  55. package/dist/tools/validation/index.js +183 -0
  56. package/dist/tools/validation/types.js +4 -0
  57. package/dist/tools/viewport/index.js +310 -0
  58. package/dist/tools/worldpartition/index.js +226 -0
  59. package/dist/tools/worldpartition/types.js +4 -0
  60. package/dist/utils/execFileNoThrow.js +40 -0
  61. package/dist/utils/logger.js +27 -0
  62. package/dist/utils/path-guard.js +26 -0
  63. package/package.json +40 -0
  64. package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
  65. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
  66. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
  67. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
  68. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
  69. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
  70. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
  71. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
  72. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
  73. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
  74. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
  75. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
  76. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
  77. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
  78. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
  79. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
  80. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
  81. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
  82. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
  83. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
  84. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
  85. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
  86. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
  87. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
  88. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
  89. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
  90. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
  91. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
  92. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
  93. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
  94. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
  95. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
  96. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
  97. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
  98. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
  99. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
  100. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
  101. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
  102. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
  103. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
  104. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
  105. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
  106. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
  107. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
  108. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
  109. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
  110. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
  111. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
  112. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
  113. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
  114. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
  115. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
  116. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
  117. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
  118. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
  119. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
  120. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
  121. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
  122. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
  123. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
  124. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
@@ -0,0 +1,110 @@
1
+ // src/docs/doc-index.ts
2
+ // DocIndex — MiniSearch-backed search engine for the UE 5.7 API reference.
3
+ // Wraps MiniSearch with four query methods: search, lookupClass, getIncludePath, checkDeprecation.
4
+ import MiniSearch from 'minisearch';
5
+ /**
6
+ * DocIndex wraps MiniSearch to provide full-text search and exact lookups
7
+ * over the curated UE 5.7 API record set.
8
+ *
9
+ * Usage:
10
+ * const index = new DocIndex();
11
+ * index.load(records); // call once at startup
12
+ * index.search('StaticMesh'); // ranked full-text results
13
+ */
14
+ export class DocIndex {
15
+ miniSearch;
16
+ byId = new Map();
17
+ byClass = new Map();
18
+ constructor() {
19
+ this.miniSearch = new MiniSearch({
20
+ idField: 'id',
21
+ fields: ['name', 'fullName', 'description', 'signature', 'parameters', 'className'],
22
+ storeFields: [
23
+ 'id',
24
+ 'type',
25
+ 'name',
26
+ 'fullName',
27
+ 'className',
28
+ 'signature',
29
+ 'returnType',
30
+ 'parameters',
31
+ 'includePath',
32
+ 'module',
33
+ 'deprecated',
34
+ 'deprecatedMessage',
35
+ 'replacementAPI',
36
+ 'description',
37
+ ],
38
+ });
39
+ }
40
+ /**
41
+ * Load ApiRecord array into the index. Call once at startup.
42
+ * Builds both the MiniSearch full-text index and the O(1) lookup maps.
43
+ */
44
+ load(records) {
45
+ if (records.length === 0)
46
+ return;
47
+ this.miniSearch.addAll(records);
48
+ for (const record of records) {
49
+ this.byId.set(record.id, record);
50
+ const key = record.className.toLowerCase();
51
+ const existing = this.byClass.get(key);
52
+ if (existing !== undefined) {
53
+ existing.push(record);
54
+ }
55
+ else {
56
+ this.byClass.set(key, [record]);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Full-text search across name, fullName, description, signature, parameters.
62
+ * Returns results ranked by relevance (highest score first), up to `limit` (default 20).
63
+ * Returns [] if the index is empty or no results match.
64
+ */
65
+ search(query, limit = 20) {
66
+ if (this.byId.size === 0)
67
+ return [];
68
+ const raw = this.miniSearch.search(query, { fuzzy: 0.2, prefix: true });
69
+ return raw.slice(0, limit).map((result) => ({
70
+ record: this.byId.get(result.id),
71
+ score: result.score,
72
+ terms: result.terms,
73
+ }));
74
+ }
75
+ /**
76
+ * Exact class lookup (case-insensitive on className field).
77
+ * Returns ALL records belonging to that class (class record + its members).
78
+ */
79
+ lookupClass(className) {
80
+ return this.byClass.get(className.toLowerCase()) ?? [];
81
+ }
82
+ /**
83
+ * Returns the includePath for the first ApiRecord whose className matches (case-insensitive).
84
+ * Returns null if no match found.
85
+ */
86
+ getIncludePath(className) {
87
+ const records = this.lookupClass(className);
88
+ if (records.length === 0)
89
+ return null;
90
+ return records[0].includePath;
91
+ }
92
+ /**
93
+ * Returns deprecation status for a symbol matched by name or fullName (case-insensitive).
94
+ * Returns null if no record matched at all.
95
+ */
96
+ checkDeprecation(symbolName) {
97
+ const lower = symbolName.toLowerCase();
98
+ for (const record of this.byId.values()) {
99
+ if (record.name.toLowerCase() === lower ||
100
+ record.fullName.toLowerCase() === lower) {
101
+ return {
102
+ deprecated: record.deprecated,
103
+ message: record.deprecatedMessage,
104
+ replacement: record.replacementAPI,
105
+ };
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+ }
@@ -0,0 +1,4 @@
1
+ // src/docs/types.ts
2
+ // API reference data types for the UE 5.7 documentation index.
3
+ // Consumed by DocIndex (doc-index.ts) and the curated data file (data/ue57-api.ts).
4
+ export {};
@@ -0,0 +1,363 @@
1
+ // src/generators/class-generator.ts
2
+ // Pure synchronous template-based code generator for UE C++ classes.
3
+ //
4
+ // Produces valid UE C++ .h/.cpp file pairs for all 8 class types:
5
+ // Actor, ActorComponent, SceneComponent, GameMode, GameState,
6
+ // PlayerState, PlayerController, Interface
7
+ //
8
+ // Non-negotiable UHT constraints:
9
+ // 1. GENERATED_BODY() is the FIRST item inside every class body
10
+ // 2. ClassName.generated.h is the LAST #include in every header
11
+ //
12
+ // Security mitigations (threat model T-05-03, T-05-04):
13
+ // - className validated against ^[A-Za-z_][A-Za-z0-9_]*$ before embedding
14
+ // - moduleName stripped of non-alphanumeric chars before uppercasing
15
+ //
16
+ // No fs, no async, no side effects — pure functions only.
17
+ // No console.log — use console.error only.
18
+ const CLASS_CONFIG = {
19
+ Actor: {
20
+ defaultParent: 'AActor',
21
+ parentInclude: 'GameFramework/Actor.h',
22
+ prefix: 'A',
23
+ hasTick: true,
24
+ hasBeginPlay: true,
25
+ useTickComponent: false,
26
+ isInterface: false,
27
+ },
28
+ ActorComponent: {
29
+ defaultParent: 'UActorComponent',
30
+ parentInclude: 'Components/ActorComponent.h',
31
+ prefix: 'U',
32
+ hasTick: true,
33
+ hasBeginPlay: true,
34
+ useTickComponent: true,
35
+ isInterface: false,
36
+ },
37
+ SceneComponent: {
38
+ defaultParent: 'USceneComponent',
39
+ parentInclude: 'Components/SceneComponent.h',
40
+ prefix: 'U',
41
+ hasTick: true,
42
+ hasBeginPlay: true,
43
+ useTickComponent: true,
44
+ isInterface: false,
45
+ },
46
+ GameMode: {
47
+ defaultParent: 'AGameModeBase',
48
+ parentInclude: 'GameFramework/GameModeBase.h',
49
+ prefix: 'A',
50
+ hasTick: false,
51
+ hasBeginPlay: false,
52
+ useTickComponent: false,
53
+ isInterface: false,
54
+ },
55
+ GameState: {
56
+ defaultParent: 'AGameStateBase',
57
+ parentInclude: 'GameFramework/GameStateBase.h',
58
+ prefix: 'A',
59
+ hasTick: false,
60
+ hasBeginPlay: false,
61
+ useTickComponent: false,
62
+ isInterface: false,
63
+ },
64
+ PlayerState: {
65
+ defaultParent: 'APlayerState',
66
+ parentInclude: 'GameFramework/PlayerState.h',
67
+ prefix: 'A',
68
+ hasTick: false,
69
+ hasBeginPlay: false,
70
+ useTickComponent: false,
71
+ isInterface: false,
72
+ },
73
+ PlayerController: {
74
+ defaultParent: 'APlayerController',
75
+ parentInclude: 'GameFramework/PlayerController.h',
76
+ prefix: 'A',
77
+ hasTick: false,
78
+ hasBeginPlay: false,
79
+ useTickComponent: false,
80
+ isInterface: false,
81
+ },
82
+ Interface: {
83
+ defaultParent: 'UInterface',
84
+ parentInclude: 'UObject/Interface.h',
85
+ prefix: 'U',
86
+ hasTick: false,
87
+ hasBeginPlay: false,
88
+ useTickComponent: false,
89
+ isInterface: true,
90
+ },
91
+ };
92
+ // ---------------------------------------------------------------------------
93
+ // Security helpers (T-05-03, T-05-04)
94
+ // ---------------------------------------------------------------------------
95
+ /** Valid C++ identifier pattern */
96
+ const VALID_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
97
+ /**
98
+ * Sanitize a className for safe embedding.
99
+ * If invalid, returns a safe fallback name.
100
+ */
101
+ function sanitizeClassName(name) {
102
+ const trimmed = (name ?? '').trim();
103
+ if (VALID_IDENTIFIER_RE.test(trimmed)) {
104
+ return { safe: trimmed, wasInvalid: false };
105
+ }
106
+ // Strip everything that isn't a valid C++ identifier char
107
+ const stripped = trimmed.replace(/[^A-Za-z0-9_]/g, '');
108
+ const safe = stripped.length > 0 && /^[A-Za-z_]/.test(stripped)
109
+ ? stripped
110
+ : 'UnknownClass';
111
+ if (trimmed !== safe) {
112
+ console.error(`[class-generator] Invalid className "${trimmed}" — using safe fallback "${safe}"`);
113
+ }
114
+ return { safe, wasInvalid: true };
115
+ }
116
+ /**
117
+ * Build the API macro from moduleName.
118
+ * T-05-04: strips non-alphanumeric chars before uppercasing.
119
+ */
120
+ function buildApiMacro(moduleName) {
121
+ const stripped = (moduleName ?? '').replace(/[^a-zA-Z0-9]/g, '');
122
+ return stripped.toUpperCase() + '_API';
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Class name prefix resolution
126
+ // ---------------------------------------------------------------------------
127
+ /**
128
+ * Apply the correct UE prefix (A or U) to a class name,
129
+ * without double-prefixing if the name already starts with that letter.
130
+ */
131
+ function applyPrefix(name, prefix) {
132
+ if (name.startsWith(prefix))
133
+ return name;
134
+ return prefix + name;
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Templates
138
+ // ---------------------------------------------------------------------------
139
+ function makeActorHeader(className, parentClass, parentInclude, apiMacro, config) {
140
+ const tickDecl = config.hasTick
141
+ ? '\npublic:\n\tvirtual void Tick(float DeltaTime) override;\n'
142
+ : '';
143
+ const beginPlayDecl = config.hasBeginPlay
144
+ ? '\nprotected:\n\tvirtual void BeginPlay() override;\n'
145
+ : '';
146
+ return `#pragma once
147
+
148
+ #include "CoreMinimal.h"
149
+ #include "${parentInclude}"
150
+ #include "${className}.generated.h"
151
+
152
+ UCLASS(BlueprintType, Blueprintable)
153
+ class ${apiMacro} ${className} : public ${parentClass}
154
+ {
155
+ \tGENERATED_BODY()
156
+
157
+ public:
158
+ \t${className}();
159
+ ${beginPlayDecl}${tickDecl}};
160
+ `;
161
+ }
162
+ function makeActorCpp(className, config) {
163
+ const beginPlayImpl = config.hasBeginPlay
164
+ ? `
165
+ void ${className}::BeginPlay()
166
+ {
167
+ \tSuper::BeginPlay();
168
+ }
169
+ `
170
+ : '';
171
+ const tickImpl = config.hasTick && !config.useTickComponent
172
+ ? `
173
+ void ${className}::Tick(float DeltaTime)
174
+ {
175
+ \tSuper::Tick(DeltaTime);
176
+ }
177
+ `
178
+ : '';
179
+ const tickComponentImpl = config.hasTick && config.useTickComponent
180
+ ? `
181
+ void ${className}::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
182
+ {
183
+ \tSuper::TickComponent(DeltaTime, TickType, ThisTickFunction);
184
+ }
185
+ `
186
+ : '';
187
+ const constructorBody = config.useTickComponent
188
+ ? `\tPrimaryComponentTick.bCanEverTick = true;`
189
+ : `\tPrimaryActorTick.bCanEverTick = true;`;
190
+ return `#include "${className}.h"
191
+
192
+ ${className}::${className}()
193
+ {
194
+ ${constructorBody}
195
+ }
196
+ ${beginPlayImpl}${tickImpl}${tickComponentImpl}`;
197
+ }
198
+ function makeComponentHeader(className, parentClass, parentInclude, apiMacro, config) {
199
+ const tickDecl = config.hasTick && config.useTickComponent
200
+ ? '\npublic:\n\tvirtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;\n'
201
+ : '';
202
+ const beginPlayDecl = config.hasBeginPlay
203
+ ? '\nprotected:\n\tvirtual void BeginPlay() override;\n'
204
+ : '';
205
+ return `#pragma once
206
+
207
+ #include "CoreMinimal.h"
208
+ #include "${parentInclude}"
209
+ #include "${className}.generated.h"
210
+
211
+ UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
212
+ class ${apiMacro} ${className} : public ${parentClass}
213
+ {
214
+ \tGENERATED_BODY()
215
+
216
+ public:
217
+ \t${className}();
218
+ ${beginPlayDecl}${tickDecl}};
219
+ `;
220
+ }
221
+ function makeSimpleHeader(className, parentClass, parentInclude, apiMacro) {
222
+ return `#pragma once
223
+
224
+ #include "CoreMinimal.h"
225
+ #include "${parentInclude}"
226
+ #include "${className}.generated.h"
227
+
228
+ UCLASS(BlueprintType, Blueprintable)
229
+ class ${apiMacro} ${className} : public ${parentClass}
230
+ {
231
+ \tGENERATED_BODY()
232
+
233
+ public:
234
+ \t${className}();
235
+ };
236
+ `;
237
+ }
238
+ function makeSimpleCpp(className) {
239
+ return `#include "${className}.h"
240
+
241
+ ${className}::${className}()
242
+ {
243
+ }
244
+ `;
245
+ }
246
+ function makeInterfaceHeader(baseName, parentInclude, apiMacro) {
247
+ // baseName is the stripped name (without U or I prefix)
248
+ // e.g. if className was "MyInterface", baseName = "MyInterface"
249
+ // We need UMyInterface and IMyInterface
250
+ const uName = `U${baseName}`;
251
+ const iName = `I${baseName}`;
252
+ const generatedInclude = `${iName}.generated.h`;
253
+ return `#pragma once
254
+
255
+ #include "CoreMinimal.h"
256
+ #include "${parentInclude}"
257
+ #include "${generatedInclude}"
258
+
259
+ UCLASS(MinimalAPI)
260
+ class ${apiMacro} ${uName} : public UInterface
261
+ {
262
+ \tGENERATED_BODY()
263
+ };
264
+
265
+ class ${apiMacro} ${iName}
266
+ {
267
+ \tGENERATED_BODY()
268
+
269
+ public:
270
+ \t// Add interface functions here
271
+ };
272
+ `;
273
+ }
274
+ function makeInterfaceCpp(baseName) {
275
+ const iName = `I${baseName}`;
276
+ return `#include "${iName}.h"
277
+
278
+ // Interface implementation stub
279
+ // Add default implementations here if needed
280
+ `;
281
+ }
282
+ // ---------------------------------------------------------------------------
283
+ // Main export
284
+ // ---------------------------------------------------------------------------
285
+ /**
286
+ * Generate a UE C++ class file pair (.h + .cpp) for the given class type.
287
+ * Never throws — returns GeneratedClass even for invalid input.
288
+ */
289
+ export function generateClass(options) {
290
+ try {
291
+ return _generateClass(options);
292
+ }
293
+ catch (err) {
294
+ console.error('[class-generator] Unexpected error — returning minimal output:', err);
295
+ const fallbackName = 'UnknownClass';
296
+ return {
297
+ header: `#pragma once\n\n// Generation failed\n`,
298
+ cpp: `// Generation failed\n`,
299
+ headerFileName: `${fallbackName}.h`,
300
+ cppFileName: `${fallbackName}.cpp`,
301
+ };
302
+ }
303
+ }
304
+ function _generateClass(options) {
305
+ const { classType, moduleName, parentClass: parentClassOverride } = options;
306
+ const config = CLASS_CONFIG[classType];
307
+ // T-05-04: sanitize moduleName
308
+ const apiMacro = buildApiMacro(moduleName);
309
+ // T-05-03: sanitize className
310
+ const { safe: safeClassName } = sanitizeClassName(options.className);
311
+ // --- Interface: special dual-class pattern ---
312
+ if (config.isInterface) {
313
+ // Strip any existing U/I prefix to get the base name
314
+ let baseName = safeClassName;
315
+ if (baseName.startsWith('U') || baseName.startsWith('I')) {
316
+ // Only strip single-letter prefix if the rest is still a valid name
317
+ const rest = baseName.slice(1);
318
+ if (rest.length > 0) {
319
+ baseName = rest;
320
+ }
321
+ }
322
+ // Canonical file names use I prefix (the "real" interface class)
323
+ const iName = `I${baseName}`;
324
+ const header = makeInterfaceHeader(baseName, config.parentInclude, apiMacro);
325
+ const cpp = makeInterfaceCpp(baseName);
326
+ return {
327
+ header,
328
+ cpp,
329
+ headerFileName: `${iName}.h`,
330
+ cppFileName: `${iName}.cpp`,
331
+ };
332
+ }
333
+ // --- Non-interface classes ---
334
+ // Apply correct prefix (A or U) without double-prefixing
335
+ const fullClassName = applyPrefix(safeClassName, config.prefix);
336
+ // Resolve parent class
337
+ const parentClass = (parentClassOverride && parentClassOverride.trim())
338
+ ? parentClassOverride.trim()
339
+ : config.defaultParent;
340
+ // Build header and cpp based on class family
341
+ let header;
342
+ let cpp;
343
+ const isComponent = classType === 'ActorComponent' || classType === 'SceneComponent';
344
+ if (isComponent) {
345
+ header = makeComponentHeader(fullClassName, parentClass, config.parentInclude, apiMacro, config);
346
+ cpp = makeActorCpp(fullClassName, config);
347
+ }
348
+ else if (classType === 'Actor') {
349
+ header = makeActorHeader(fullClassName, parentClass, config.parentInclude, apiMacro, config);
350
+ cpp = makeActorCpp(fullClassName, config);
351
+ }
352
+ else {
353
+ // GameMode, GameState, PlayerState, PlayerController — no Tick, no BeginPlay
354
+ header = makeSimpleHeader(fullClassName, parentClass, config.parentInclude, apiMacro);
355
+ cpp = makeSimpleCpp(fullClassName);
356
+ }
357
+ return {
358
+ header,
359
+ cpp,
360
+ headerFileName: `${fullClassName}.h`,
361
+ cppFileName: `${fullClassName}.cpp`,
362
+ };
363
+ }