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.
- package/README.md +729 -0
- package/dist/build/error-parser.js +51 -0
- package/dist/build/fix-suggester.js +84 -0
- package/dist/build/ubt-runner.js +146 -0
- package/dist/cli.js +13 -0
- package/dist/config.js +8 -0
- package/dist/docs/data/ue57-api.js +228 -0
- package/dist/docs/doc-index.js +110 -0
- package/dist/docs/types.js +4 -0
- package/dist/generators/class-generator.js +363 -0
- package/dist/generators/file-modifier.js +276 -0
- package/dist/generators/uht-validator.js +177 -0
- package/dist/index.js +89 -0
- package/dist/parsers/cpp-class-index.js +230 -0
- package/dist/parsers/cpp-parser.js +369 -0
- package/dist/parsers/ini-parser.js +216 -0
- package/dist/parsers/uproject-parser.js +130 -0
- package/dist/plugin-bridge/client.js +217 -0
- package/dist/plugin-bridge/protocol.js +6 -0
- package/dist/plugin-bridge/retry.js +23 -0
- package/dist/setup.js +209 -0
- package/dist/tools/ai-systems/index.js +247 -0
- package/dist/tools/ai-systems/types.js +4 -0
- package/dist/tools/animation/index.js +241 -0
- package/dist/tools/animation/types.js +4 -0
- package/dist/tools/audio/index.js +204 -0
- package/dist/tools/audio/types.js +4 -0
- package/dist/tools/blueprint/index.js +495 -0
- package/dist/tools/blueprint/types.js +4 -0
- package/dist/tools/build/index.js +163 -0
- package/dist/tools/chaos/index.js +230 -0
- package/dist/tools/chaos/types.js +4 -0
- package/dist/tools/collision-physics/index.js +211 -0
- package/dist/tools/config/index.js +288 -0
- package/dist/tools/cpp/index.js +305 -0
- package/dist/tools/docs/index.js +251 -0
- package/dist/tools/editor/index.js +242 -0
- package/dist/tools/gas/index.js +222 -0
- package/dist/tools/gas/types.js +5 -0
- package/dist/tools/import-export/index.js +218 -0
- package/dist/tools/input/index.js +146 -0
- package/dist/tools/known-issues/index.js +88 -0
- package/dist/tools/known-issues/middleware.js +55 -0
- package/dist/tools/known-issues/store.js +125 -0
- package/dist/tools/livelink/index.js +203 -0
- package/dist/tools/livelink/types.js +4 -0
- package/dist/tools/material/index.js +190 -0
- package/dist/tools/motion-design/index.js +251 -0
- package/dist/tools/motion-design/types.js +6 -0
- package/dist/tools/movie-render/index.js +220 -0
- package/dist/tools/networking/index.js +149 -0
- package/dist/tools/pcg/index.js +164 -0
- package/dist/tools/selection/index.js +180 -0
- package/dist/tools/sequencer/index.js +218 -0
- package/dist/tools/validation/index.js +183 -0
- package/dist/tools/validation/types.js +4 -0
- package/dist/tools/viewport/index.js +310 -0
- package/dist/tools/worldpartition/index.js +226 -0
- package/dist/tools/worldpartition/types.js +4 -0
- package/dist/utils/execFileNoThrow.js +40 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/path-guard.js +26 -0
- package/package.json +40 -0
- package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
- 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,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
|
+
}
|