unreal-engine-mcp-server 0.3.1 → 0.4.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/.env.production +1 -1
- package/README.md +1 -2
- package/dist/index.js +16 -18
- package/dist/resources/assets.d.ts +3 -2
- package/dist/resources/assets.js +96 -72
- package/dist/resources/levels.js +2 -2
- package/dist/tools/assets.js +6 -2
- package/dist/tools/build_environment_advanced.js +46 -42
- package/dist/tools/consolidated-tool-definitions.d.ts +232 -15
- package/dist/tools/consolidated-tool-definitions.js +46 -3
- package/dist/tools/consolidated-tool-handlers.js +327 -717
- package/dist/tools/debug.js +4 -6
- package/dist/tools/rc.js +2 -2
- package/dist/tools/sequence.js +21 -2
- package/dist/utils/response-validator.d.ts +6 -1
- package/dist/utils/response-validator.js +41 -14
- package/package.json +4 -4
- package/server.json +2 -2
- package/src/index.ts +18 -19
- package/src/resources/assets.ts +97 -73
- package/src/resources/levels.ts +2 -2
- package/src/tools/assets.ts +6 -2
- package/src/tools/build_environment_advanced.ts +46 -42
- package/src/tools/consolidated-tool-definitions.ts +46 -3
- package/src/tools/consolidated-tool-handlers.ts +313 -746
- package/src/tools/debug.ts +4 -6
- package/src/tools/rc.ts +2 -2
- package/src/tools/sequence.ts +21 -2
- package/src/utils/response-validator.ts +46 -18
- package/dist/tools/tool-definitions.d.ts +0 -4919
- package/dist/tools/tool-definitions.js +0 -1065
- package/dist/tools/tool-handlers.d.ts +0 -47
- package/dist/tools/tool-handlers.js +0 -863
- package/src/tools/tool-definitions.ts +0 -1081
- package/src/tools/tool-handlers.ts +0 -973
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Consolidated tool handlers - maps 13 tools to all 36 operations
|
|
2
|
-
import { handleToolCall } from './tool-handlers.js';
|
|
3
2
|
import { cleanObject } from '../utils/safe-json.js';
|
|
4
3
|
import { Logger } from '../utils/logger.js';
|
|
5
4
|
const log = new Logger('ConsolidatedToolHandler');
|
|
@@ -12,8 +11,6 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
12
11
|
if (args === null || args === undefined) {
|
|
13
12
|
throw new Error('Invalid arguments: null or undefined');
|
|
14
13
|
}
|
|
15
|
-
let mappedName;
|
|
16
|
-
let mappedArgs = { ...args };
|
|
17
14
|
switch (name) {
|
|
18
15
|
// 1. ASSET MANAGER
|
|
19
16
|
case 'manage_asset':
|
|
@@ -26,72 +23,33 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
26
23
|
throw new Error('Missing required parameter: action');
|
|
27
24
|
}
|
|
28
25
|
switch (args.action) {
|
|
29
|
-
case 'list':
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (typeof args.directory !== 'string') {
|
|
33
|
-
throw new Error('Invalid directory: must be a string');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
mappedName = 'list_assets';
|
|
37
|
-
mappedArgs = {
|
|
38
|
-
directory: args.directory
|
|
39
|
-
// recursive removed - always false internally
|
|
40
|
-
};
|
|
41
|
-
break;
|
|
42
|
-
case 'import':
|
|
43
|
-
// Validate required parameters
|
|
44
|
-
if (args.sourcePath === undefined || args.sourcePath === null) {
|
|
45
|
-
throw new Error('Missing required parameter: sourcePath');
|
|
46
|
-
}
|
|
47
|
-
if (typeof args.sourcePath !== 'string') {
|
|
48
|
-
throw new Error('Invalid sourcePath: must be a string');
|
|
49
|
-
}
|
|
50
|
-
if (args.sourcePath.trim() === '') {
|
|
51
|
-
throw new Error('Invalid sourcePath: cannot be empty');
|
|
52
|
-
}
|
|
53
|
-
if (args.destinationPath === undefined || args.destinationPath === null) {
|
|
54
|
-
throw new Error('Missing required parameter: destinationPath');
|
|
26
|
+
case 'list': {
|
|
27
|
+
if (args.directory !== undefined && args.directory !== null && typeof args.directory !== 'string') {
|
|
28
|
+
throw new Error('Invalid directory: must be a string');
|
|
55
29
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
mappedName = 'import_asset';
|
|
63
|
-
mappedArgs = {
|
|
64
|
-
sourcePath: args.sourcePath,
|
|
65
|
-
destinationPath: args.destinationPath
|
|
66
|
-
};
|
|
67
|
-
break;
|
|
68
|
-
case 'create_material':
|
|
69
|
-
// Validate required parameters
|
|
70
|
-
if (args.name === undefined || args.name === null) {
|
|
71
|
-
throw new Error('Missing required parameter: name');
|
|
72
|
-
}
|
|
73
|
-
if (typeof args.name !== 'string') {
|
|
74
|
-
throw new Error('Invalid name: must be a string');
|
|
30
|
+
const res = await tools.assetResources.list(args.directory || '/Game', false);
|
|
31
|
+
return cleanObject({ success: true, ...res });
|
|
32
|
+
}
|
|
33
|
+
case 'import': {
|
|
34
|
+
if (typeof args.sourcePath !== 'string' || args.sourcePath.trim() === '') {
|
|
35
|
+
throw new Error('Invalid sourcePath');
|
|
75
36
|
}
|
|
76
|
-
if (args.
|
|
77
|
-
throw new Error('Invalid
|
|
37
|
+
if (typeof args.destinationPath !== 'string' || args.destinationPath.trim() === '') {
|
|
38
|
+
throw new Error('Invalid destinationPath');
|
|
78
39
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
40
|
+
const res = await tools.assetTools.importAsset(args.sourcePath, args.destinationPath);
|
|
41
|
+
return cleanObject(res);
|
|
42
|
+
}
|
|
43
|
+
case 'create_material': {
|
|
44
|
+
if (typeof args.name !== 'string' || args.name.trim() === '') {
|
|
45
|
+
throw new Error('Invalid name: must be a non-empty string');
|
|
84
46
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
path: args.path
|
|
89
|
-
};
|
|
90
|
-
break;
|
|
47
|
+
const res = await tools.materialTools.createMaterial(args.name, args.path || '/Game/Materials');
|
|
48
|
+
return cleanObject(res);
|
|
49
|
+
}
|
|
91
50
|
default:
|
|
92
51
|
throw new Error(`Unknown asset action: ${args.action}`);
|
|
93
52
|
}
|
|
94
|
-
break;
|
|
95
53
|
// 2. ACTOR CONTROL
|
|
96
54
|
case 'control_actor':
|
|
97
55
|
// Validate action exists
|
|
@@ -99,63 +57,41 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
99
57
|
throw new Error('Missing required parameter: action');
|
|
100
58
|
}
|
|
101
59
|
switch (args.action) {
|
|
102
|
-
case 'spawn':
|
|
103
|
-
|
|
104
|
-
if (!args.classPath) {
|
|
105
|
-
throw new Error('Missing required parameter: classPath');
|
|
106
|
-
}
|
|
107
|
-
if (typeof args.classPath !== 'string' || args.classPath.trim() === '') {
|
|
60
|
+
case 'spawn': {
|
|
61
|
+
if (!args.classPath || typeof args.classPath !== 'string' || args.classPath.trim() === '') {
|
|
108
62
|
throw new Error('Invalid classPath: must be a non-empty string');
|
|
109
63
|
}
|
|
110
|
-
|
|
111
|
-
mappedArgs = {
|
|
64
|
+
const res = await tools.actorTools.spawn({
|
|
112
65
|
classPath: args.classPath,
|
|
113
66
|
location: args.location,
|
|
114
67
|
rotation: args.rotation
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!args.actorName) {
|
|
120
|
-
throw new Error('
|
|
121
|
-
}
|
|
122
|
-
if (typeof args.actorName !== 'string' || args.actorName.trim() === '') {
|
|
123
|
-
throw new Error('Invalid actorName: must be a non-empty string');
|
|
124
|
-
}
|
|
125
|
-
mappedName = 'delete_actor';
|
|
126
|
-
mappedArgs = {
|
|
127
|
-
actorName: args.actorName
|
|
128
|
-
};
|
|
129
|
-
break;
|
|
130
|
-
case 'apply_force':
|
|
131
|
-
// Validate apply_force parameters
|
|
132
|
-
if (!args.actorName) {
|
|
133
|
-
throw new Error('Missing required parameter: actorName');
|
|
134
|
-
}
|
|
135
|
-
if (typeof args.actorName !== 'string' || args.actorName.trim() === '') {
|
|
136
|
-
throw new Error('Invalid actorName: must be a non-empty string');
|
|
137
|
-
}
|
|
138
|
-
if (!args.force) {
|
|
139
|
-
throw new Error('Missing required parameter: force');
|
|
68
|
+
});
|
|
69
|
+
return cleanObject(res);
|
|
70
|
+
}
|
|
71
|
+
case 'delete': {
|
|
72
|
+
if (!args.actorName || typeof args.actorName !== 'string' || args.actorName.trim() === '') {
|
|
73
|
+
throw new Error('Invalid actorName');
|
|
140
74
|
}
|
|
141
|
-
|
|
142
|
-
|
|
75
|
+
const res = await tools.bridge.executeEditorFunction('DELETE_ACTOR', { actor_name: args.actorName });
|
|
76
|
+
return cleanObject(res);
|
|
77
|
+
}
|
|
78
|
+
case 'apply_force': {
|
|
79
|
+
if (!args.actorName || typeof args.actorName !== 'string' || args.actorName.trim() === '') {
|
|
80
|
+
throw new Error('Invalid actorName');
|
|
143
81
|
}
|
|
144
|
-
if (typeof args.force.x !== 'number' ||
|
|
145
|
-
|
|
146
|
-
typeof args.force.z !== 'number') {
|
|
147
|
-
throw new Error('Invalid force: x, y, z must all be numbers');
|
|
82
|
+
if (!args.force || typeof args.force.x !== 'number' || typeof args.force.y !== 'number' || typeof args.force.z !== 'number') {
|
|
83
|
+
throw new Error('Invalid force: must have numeric x,y,z');
|
|
148
84
|
}
|
|
149
|
-
|
|
150
|
-
mappedArgs = {
|
|
85
|
+
const res = await tools.physicsTools.applyForce({
|
|
151
86
|
actorName: args.actorName,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
87
|
+
forceType: 'Force',
|
|
88
|
+
vector: [args.force.x, args.force.y, args.force.z]
|
|
89
|
+
});
|
|
90
|
+
return cleanObject(res);
|
|
91
|
+
}
|
|
155
92
|
default:
|
|
156
93
|
throw new Error(`Unknown actor action: ${args.action}`);
|
|
157
94
|
}
|
|
158
|
-
break;
|
|
159
95
|
// 3. EDITOR CONTROL
|
|
160
96
|
case 'control_editor':
|
|
161
97
|
// Validate action exists
|
|
@@ -163,210 +99,91 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
163
99
|
throw new Error('Missing required parameter: action');
|
|
164
100
|
}
|
|
165
101
|
switch (args.action) {
|
|
166
|
-
case 'play':
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
case 'stop':
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
case 'pause':
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
case 'set_game_speed':
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
throw new Error('Missing required parameter: speed');
|
|
182
|
-
}
|
|
183
|
-
if (typeof args.speed !== 'number') {
|
|
184
|
-
throw new Error('Invalid speed: must be a number');
|
|
185
|
-
}
|
|
186
|
-
if (isNaN(args.speed)) {
|
|
187
|
-
throw new Error('Invalid speed: cannot be NaN');
|
|
188
|
-
}
|
|
189
|
-
if (!isFinite(args.speed)) {
|
|
190
|
-
throw new Error('Invalid speed: must be finite');
|
|
191
|
-
}
|
|
192
|
-
if (args.speed <= 0) {
|
|
193
|
-
throw new Error('Invalid speed: must be positive');
|
|
102
|
+
case 'play': {
|
|
103
|
+
const res = await tools.editorTools.playInEditor();
|
|
104
|
+
return cleanObject(res);
|
|
105
|
+
}
|
|
106
|
+
case 'stop': {
|
|
107
|
+
const res = await tools.editorTools.stopPlayInEditor();
|
|
108
|
+
return cleanObject(res);
|
|
109
|
+
}
|
|
110
|
+
case 'pause': {
|
|
111
|
+
const res = await tools.editorTools.pausePlayInEditor();
|
|
112
|
+
return cleanObject(res);
|
|
113
|
+
}
|
|
114
|
+
case 'set_game_speed': {
|
|
115
|
+
if (typeof args.speed !== 'number' || !isFinite(args.speed) || args.speed <= 0) {
|
|
116
|
+
throw new Error('Invalid speed: must be a positive number');
|
|
194
117
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
location: args.location,
|
|
214
|
-
rotation: args.rotation
|
|
215
|
-
};
|
|
216
|
-
break;
|
|
217
|
-
case 'set_view_mode':
|
|
218
|
-
// Validate view mode parameter
|
|
219
|
-
if (!args.viewMode) {
|
|
118
|
+
// Use console command via bridge
|
|
119
|
+
const res = await tools.bridge.executeConsoleCommand(`slomo ${args.speed}`);
|
|
120
|
+
return cleanObject(res);
|
|
121
|
+
}
|
|
122
|
+
case 'eject': {
|
|
123
|
+
const res = await tools.bridge.executeConsoleCommand('eject');
|
|
124
|
+
return cleanObject(res);
|
|
125
|
+
}
|
|
126
|
+
case 'possess': {
|
|
127
|
+
const res = await tools.bridge.executeConsoleCommand('viewself');
|
|
128
|
+
return cleanObject(res);
|
|
129
|
+
}
|
|
130
|
+
case 'set_camera': {
|
|
131
|
+
const res = await tools.editorTools.setViewportCamera(args.location, args.rotation);
|
|
132
|
+
return cleanObject(res);
|
|
133
|
+
}
|
|
134
|
+
case 'set_view_mode': {
|
|
135
|
+
if (!args.viewMode || typeof args.viewMode !== 'string')
|
|
220
136
|
throw new Error('Missing required parameter: viewMode');
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
// Normalize view mode to match what debug.ts expects
|
|
226
|
-
const validModes = ['lit', 'unlit', 'wireframe', 'detail_lighting', 'lighting_only',
|
|
227
|
-
'light_complexity', 'shader_complexity', 'lightmap_density',
|
|
228
|
-
'stationary_light_overlap', 'reflections', 'visualize_buffer',
|
|
229
|
-
'collision_pawn', 'collision_visibility', 'lod_coloration', 'quad_overdraw'];
|
|
230
|
-
const normalizedMode = args.viewMode.toLowerCase().replace(/_/g, '');
|
|
231
|
-
// Map to proper case for debug.ts
|
|
232
|
-
let mappedMode = '';
|
|
233
|
-
switch (normalizedMode) {
|
|
234
|
-
case 'lit':
|
|
235
|
-
mappedMode = 'Lit';
|
|
236
|
-
break;
|
|
237
|
-
case 'unlit':
|
|
238
|
-
mappedMode = 'Unlit';
|
|
239
|
-
break;
|
|
240
|
-
case 'wireframe':
|
|
241
|
-
mappedMode = 'Wireframe';
|
|
242
|
-
break;
|
|
243
|
-
case 'detaillighting':
|
|
244
|
-
mappedMode = 'DetailLighting';
|
|
245
|
-
break;
|
|
246
|
-
case 'lightingonly':
|
|
247
|
-
mappedMode = 'LightingOnly';
|
|
248
|
-
break;
|
|
249
|
-
case 'lightcomplexity':
|
|
250
|
-
mappedMode = 'LightComplexity';
|
|
251
|
-
break;
|
|
252
|
-
case 'shadercomplexity':
|
|
253
|
-
mappedMode = 'ShaderComplexity';
|
|
254
|
-
break;
|
|
255
|
-
case 'lightmapdensity':
|
|
256
|
-
mappedMode = 'LightmapDensity';
|
|
257
|
-
break;
|
|
258
|
-
case 'stationarylightoverlap':
|
|
259
|
-
mappedMode = 'StationaryLightOverlap';
|
|
260
|
-
break;
|
|
261
|
-
case 'reflections':
|
|
262
|
-
mappedMode = 'ReflectionOverride';
|
|
263
|
-
break;
|
|
264
|
-
case 'visualizebuffer':
|
|
265
|
-
mappedMode = 'VisualizeBuffer';
|
|
266
|
-
break;
|
|
267
|
-
case 'collisionpawn':
|
|
268
|
-
mappedMode = 'CollisionPawn';
|
|
269
|
-
break;
|
|
270
|
-
case 'collisionvisibility':
|
|
271
|
-
mappedMode = 'CollisionVisibility';
|
|
272
|
-
break;
|
|
273
|
-
case 'lodcoloration':
|
|
274
|
-
mappedMode = 'LODColoration';
|
|
275
|
-
break;
|
|
276
|
-
case 'quadoverdraw':
|
|
277
|
-
mappedMode = 'QuadOverdraw';
|
|
278
|
-
break;
|
|
279
|
-
default:
|
|
280
|
-
throw new Error(`Invalid viewMode: '${args.viewMode}'. Valid modes are: ${validModes.join(', ')}`);
|
|
281
|
-
}
|
|
282
|
-
mappedName = 'set_view_mode';
|
|
283
|
-
mappedArgs = {
|
|
284
|
-
mode: mappedMode
|
|
285
|
-
};
|
|
286
|
-
break;
|
|
137
|
+
const res = await tools.bridge.setSafeViewMode(args.viewMode);
|
|
138
|
+
return cleanObject(res);
|
|
139
|
+
}
|
|
287
140
|
default:
|
|
288
141
|
throw new Error(`Unknown editor action: ${args.action}`);
|
|
289
142
|
}
|
|
290
|
-
break;
|
|
291
143
|
// 4. LEVEL MANAGER
|
|
292
144
|
case 'manage_level':
|
|
145
|
+
if (!args.action)
|
|
146
|
+
throw new Error('Missing required parameter: action');
|
|
293
147
|
switch (args.action) {
|
|
294
|
-
case 'load':
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case 'stream':
|
|
313
|
-
mappedName = 'stream_level';
|
|
314
|
-
mappedArgs = {
|
|
315
|
-
levelName: args.levelName,
|
|
316
|
-
shouldBeLoaded: args.shouldBeLoaded,
|
|
317
|
-
shouldBeVisible: args.shouldBeVisible
|
|
318
|
-
};
|
|
319
|
-
break;
|
|
320
|
-
case 'create_light':
|
|
321
|
-
// Validate light type
|
|
322
|
-
if (!args.lightType) {
|
|
148
|
+
case 'load': {
|
|
149
|
+
if (!args.levelPath || typeof args.levelPath !== 'string')
|
|
150
|
+
throw new Error('Missing required parameter: levelPath');
|
|
151
|
+
const res = await tools.levelTools.loadLevel({ levelPath: args.levelPath, streaming: !!args.streaming });
|
|
152
|
+
return cleanObject(res);
|
|
153
|
+
}
|
|
154
|
+
case 'save': {
|
|
155
|
+
const res = await tools.levelTools.saveLevel({ levelName: args.levelName, savePath: args.savePath });
|
|
156
|
+
return cleanObject(res);
|
|
157
|
+
}
|
|
158
|
+
case 'stream': {
|
|
159
|
+
if (!args.levelName || typeof args.levelName !== 'string')
|
|
160
|
+
throw new Error('Missing required parameter: levelName');
|
|
161
|
+
const res = await tools.levelTools.streamLevel({ levelName: args.levelName, shouldBeLoaded: !!args.shouldBeLoaded, shouldBeVisible: !!args.shouldBeVisible });
|
|
162
|
+
return cleanObject(res);
|
|
163
|
+
}
|
|
164
|
+
case 'create_light': {
|
|
165
|
+
if (!args.lightType)
|
|
323
166
|
throw new Error('Missing required parameter: lightType');
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
342
|
-
if (args.intensity < 0) {
|
|
343
|
-
throw new Error('Invalid intensity: must be non-negative');
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// Validate location if provided
|
|
347
|
-
if (args.location !== undefined && args.location !== null) {
|
|
348
|
-
if (!Array.isArray(args.location) && typeof args.location !== 'object') {
|
|
349
|
-
throw new Error('Invalid location: must be an array [x,y,z] or object {x,y,z}');
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
mappedName = 'create_light';
|
|
353
|
-
mappedArgs = {
|
|
354
|
-
lightType: args.lightType,
|
|
355
|
-
name: args.name,
|
|
356
|
-
location: args.location,
|
|
357
|
-
intensity: args.intensity
|
|
358
|
-
};
|
|
359
|
-
break;
|
|
360
|
-
case 'build_lighting':
|
|
361
|
-
mappedName = 'build_lighting';
|
|
362
|
-
mappedArgs = {
|
|
363
|
-
quality: args.quality
|
|
364
|
-
};
|
|
365
|
-
break;
|
|
167
|
+
if (!args.name || typeof args.name !== 'string' || args.name.trim() === '')
|
|
168
|
+
throw new Error('Invalid name');
|
|
169
|
+
const t = String(args.lightType).toLowerCase();
|
|
170
|
+
if (t === 'directional')
|
|
171
|
+
return cleanObject(await tools.lightingTools.createDirectionalLight({ name: args.name, intensity: args.intensity }));
|
|
172
|
+
if (t === 'point')
|
|
173
|
+
return cleanObject(await tools.lightingTools.createPointLight({ name: args.name, location: args.location ? [args.location.x, args.location.y, args.location.z] : [0, 0, 0], intensity: args.intensity }));
|
|
174
|
+
if (t === 'spot')
|
|
175
|
+
return cleanObject(await tools.lightingTools.createSpotLight({ name: args.name, location: args.location ? [args.location.x, args.location.y, args.location.z] : [0, 0, 0], rotation: [0, 0, 0], intensity: args.intensity }));
|
|
176
|
+
if (t === 'rect')
|
|
177
|
+
return cleanObject(await tools.lightingTools.createRectLight({ name: args.name, location: args.location ? [args.location.x, args.location.y, args.location.z] : [0, 0, 0], rotation: [0, 0, 0], intensity: args.intensity }));
|
|
178
|
+
throw new Error(`Unknown light type: ${args.lightType}`);
|
|
179
|
+
}
|
|
180
|
+
case 'build_lighting': {
|
|
181
|
+
const res = await tools.lightingTools.buildLighting({ quality: args.quality || 'High', buildReflectionCaptures: true });
|
|
182
|
+
return cleanObject(res);
|
|
183
|
+
}
|
|
366
184
|
default:
|
|
367
185
|
throw new Error(`Unknown level action: ${args.action}`);
|
|
368
186
|
}
|
|
369
|
-
break;
|
|
370
187
|
// 5. ANIMATION & PHYSICS
|
|
371
188
|
case 'animation_physics':
|
|
372
189
|
// Validate action exists
|
|
@@ -374,438 +191,244 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
374
191
|
throw new Error('Missing required parameter: action');
|
|
375
192
|
}
|
|
376
193
|
switch (args.action) {
|
|
377
|
-
case 'create_animation_bp':
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (args.skeletonPath === undefined || args.skeletonPath === null) {
|
|
389
|
-
throw new Error('Missing required parameter: skeletonPath');
|
|
390
|
-
}
|
|
391
|
-
if (typeof args.skeletonPath !== 'string') {
|
|
392
|
-
throw new Error('Invalid skeletonPath: must be a string');
|
|
393
|
-
}
|
|
394
|
-
if (args.skeletonPath.trim() === '') {
|
|
395
|
-
throw new Error('Invalid skeletonPath: cannot be empty');
|
|
396
|
-
}
|
|
397
|
-
// Optional savePath validation
|
|
398
|
-
if (args.savePath !== undefined && args.savePath !== null) {
|
|
399
|
-
if (typeof args.savePath !== 'string') {
|
|
400
|
-
throw new Error('Invalid savePath: must be a string');
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
mappedName = 'create_animation_blueprint';
|
|
404
|
-
mappedArgs = {
|
|
405
|
-
name: args.name,
|
|
406
|
-
skeletonPath: args.skeletonPath,
|
|
407
|
-
savePath: args.savePath
|
|
408
|
-
};
|
|
409
|
-
break;
|
|
410
|
-
case 'play_montage':
|
|
411
|
-
// Validate required parameters
|
|
412
|
-
if (args.actorName === undefined || args.actorName === null) {
|
|
413
|
-
throw new Error('Missing required parameter: actorName');
|
|
414
|
-
}
|
|
415
|
-
if (typeof args.actorName !== 'string') {
|
|
416
|
-
throw new Error('Invalid actorName: must be a string');
|
|
417
|
-
}
|
|
418
|
-
if (args.actorName.trim() === '') {
|
|
419
|
-
throw new Error('Invalid actorName: cannot be empty');
|
|
420
|
-
}
|
|
421
|
-
// Check for montagePath or animationPath
|
|
194
|
+
case 'create_animation_bp': {
|
|
195
|
+
if (typeof args.name !== 'string' || args.name.trim() === '')
|
|
196
|
+
throw new Error('Invalid name');
|
|
197
|
+
if (typeof args.skeletonPath !== 'string' || args.skeletonPath.trim() === '')
|
|
198
|
+
throw new Error('Invalid skeletonPath');
|
|
199
|
+
const res = await tools.animationTools.createAnimationBlueprint({ name: args.name, skeletonPath: args.skeletonPath, savePath: args.savePath });
|
|
200
|
+
return cleanObject(res);
|
|
201
|
+
}
|
|
202
|
+
case 'play_montage': {
|
|
203
|
+
if (typeof args.actorName !== 'string' || args.actorName.trim() === '')
|
|
204
|
+
throw new Error('Invalid actorName');
|
|
422
205
|
const montagePath = args.montagePath || args.animationPath;
|
|
423
|
-
if (montagePath
|
|
424
|
-
throw new Error('
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
throw new Error('Invalid
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
if (isNaN(args.playRate)) {
|
|
438
|
-
throw new Error('Invalid playRate: cannot be NaN');
|
|
439
|
-
}
|
|
440
|
-
if (!isFinite(args.playRate)) {
|
|
441
|
-
throw new Error('Invalid playRate: must be finite');
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
mappedName = 'play_animation_montage';
|
|
445
|
-
mappedArgs = {
|
|
446
|
-
actorName: args.actorName,
|
|
447
|
-
montagePath: montagePath,
|
|
448
|
-
playRate: args.playRate
|
|
449
|
-
};
|
|
450
|
-
break;
|
|
451
|
-
case 'setup_ragdoll':
|
|
452
|
-
// Validate required parameters
|
|
453
|
-
if (args.skeletonPath === undefined || args.skeletonPath === null) {
|
|
454
|
-
throw new Error('Missing required parameter: skeletonPath');
|
|
455
|
-
}
|
|
456
|
-
if (typeof args.skeletonPath !== 'string') {
|
|
457
|
-
throw new Error('Invalid skeletonPath: must be a string');
|
|
458
|
-
}
|
|
459
|
-
if (args.skeletonPath.trim() === '') {
|
|
460
|
-
throw new Error('Invalid skeletonPath: cannot be empty');
|
|
461
|
-
}
|
|
462
|
-
if (args.physicsAssetName === undefined || args.physicsAssetName === null) {
|
|
463
|
-
throw new Error('Missing required parameter: physicsAssetName');
|
|
464
|
-
}
|
|
465
|
-
if (typeof args.physicsAssetName !== 'string') {
|
|
466
|
-
throw new Error('Invalid physicsAssetName: must be a string');
|
|
467
|
-
}
|
|
468
|
-
if (args.physicsAssetName.trim() === '') {
|
|
469
|
-
throw new Error('Invalid physicsAssetName: cannot be empty');
|
|
470
|
-
}
|
|
471
|
-
// Optional blendWeight validation
|
|
472
|
-
if (args.blendWeight !== undefined && args.blendWeight !== null) {
|
|
473
|
-
if (typeof args.blendWeight !== 'number') {
|
|
474
|
-
throw new Error('Invalid blendWeight: must be a number');
|
|
475
|
-
}
|
|
476
|
-
if (isNaN(args.blendWeight)) {
|
|
477
|
-
throw new Error('Invalid blendWeight: cannot be NaN');
|
|
478
|
-
}
|
|
479
|
-
if (!isFinite(args.blendWeight)) {
|
|
480
|
-
throw new Error('Invalid blendWeight: must be finite');
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// Optional savePath validation
|
|
484
|
-
if (args.savePath !== undefined && args.savePath !== null) {
|
|
485
|
-
if (typeof args.savePath !== 'string') {
|
|
486
|
-
throw new Error('Invalid savePath: must be a string');
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
mappedName = 'setup_ragdoll';
|
|
490
|
-
mappedArgs = {
|
|
491
|
-
skeletonPath: args.skeletonPath,
|
|
492
|
-
physicsAssetName: args.physicsAssetName,
|
|
493
|
-
blendWeight: args.blendWeight,
|
|
494
|
-
savePath: args.savePath
|
|
495
|
-
};
|
|
496
|
-
break;
|
|
206
|
+
if (typeof montagePath !== 'string' || montagePath.trim() === '')
|
|
207
|
+
throw new Error('Invalid montagePath');
|
|
208
|
+
const res = await tools.animationTools.playAnimation({ actorName: args.actorName, animationType: 'Montage', animationPath: montagePath, playRate: args.playRate });
|
|
209
|
+
return cleanObject(res);
|
|
210
|
+
}
|
|
211
|
+
case 'setup_ragdoll': {
|
|
212
|
+
if (typeof args.skeletonPath !== 'string' || args.skeletonPath.trim() === '')
|
|
213
|
+
throw new Error('Invalid skeletonPath');
|
|
214
|
+
if (typeof args.physicsAssetName !== 'string' || args.physicsAssetName.trim() === '')
|
|
215
|
+
throw new Error('Invalid physicsAssetName');
|
|
216
|
+
const res = await tools.physicsTools.setupRagdoll({ skeletonPath: args.skeletonPath, physicsAssetName: args.physicsAssetName, blendWeight: args.blendWeight, savePath: args.savePath });
|
|
217
|
+
return cleanObject(res);
|
|
218
|
+
}
|
|
497
219
|
default:
|
|
498
220
|
throw new Error(`Unknown animation/physics action: ${args.action}`);
|
|
499
221
|
}
|
|
500
|
-
break;
|
|
501
222
|
// 6. EFFECTS SYSTEM
|
|
502
223
|
case 'create_effect':
|
|
503
224
|
switch (args.action) {
|
|
504
|
-
case 'particle':
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
size
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
225
|
+
case 'particle': {
|
|
226
|
+
const res = await tools.niagaraTools.createEffect({ effectType: args.effectType, name: args.name, location: args.location, scale: args.scale, customParameters: args.customParameters });
|
|
227
|
+
return cleanObject(res);
|
|
228
|
+
}
|
|
229
|
+
case 'niagara': {
|
|
230
|
+
if (typeof args.systemPath !== 'string' || args.systemPath.trim() === '')
|
|
231
|
+
throw new Error('Invalid systemPath');
|
|
232
|
+
// Create or ensure system exists (spawning in editor is not universally supported via RC)
|
|
233
|
+
const name = args.name || args.systemPath.split('/').pop();
|
|
234
|
+
const res = await tools.niagaraTools.createSystem({ name, savePath: args.savePath || '/Game/Effects/Niagara' });
|
|
235
|
+
return cleanObject(res);
|
|
236
|
+
}
|
|
237
|
+
case 'debug_shape': {
|
|
238
|
+
const shape = String(args.shape || 'Sphere').toLowerCase();
|
|
239
|
+
const loc = args.location || { x: 0, y: 0, z: 0 };
|
|
240
|
+
const size = args.size || 100;
|
|
241
|
+
const color = args.color || [255, 0, 0, 255];
|
|
242
|
+
const duration = args.duration || 5;
|
|
243
|
+
if (shape === 'line') {
|
|
244
|
+
const end = args.end || { x: loc.x + size, y: loc.y, z: loc.z };
|
|
245
|
+
return cleanObject(await tools.debugTools.drawDebugLine({ start: [loc.x, loc.y, loc.z], end: [end.x, end.y, end.z], color, duration }));
|
|
246
|
+
}
|
|
247
|
+
else if (shape === 'box') {
|
|
248
|
+
const extent = [size, size, size];
|
|
249
|
+
return cleanObject(await tools.debugTools.drawDebugBox({ center: [loc.x, loc.y, loc.z], extent, color, duration }));
|
|
250
|
+
}
|
|
251
|
+
else if (shape === 'sphere') {
|
|
252
|
+
return cleanObject(await tools.debugTools.drawDebugSphere({ center: [loc.x, loc.y, loc.z], radius: size, color, duration }));
|
|
253
|
+
}
|
|
254
|
+
else if (shape === 'capsule') {
|
|
255
|
+
return cleanObject(await tools.debugTools.drawDebugCapsule({ center: [loc.x, loc.y, loc.z], halfHeight: size, radius: Math.max(10, size / 3), color, duration }));
|
|
256
|
+
}
|
|
257
|
+
else if (shape === 'cone') {
|
|
258
|
+
return cleanObject(await tools.debugTools.drawDebugCone({ origin: [loc.x, loc.y, loc.z], direction: [0, 0, 1], length: size, angleWidth: 0.5, angleHeight: 0.5, color, duration }));
|
|
259
|
+
}
|
|
260
|
+
else if (shape === 'arrow') {
|
|
261
|
+
const end = args.end || { x: loc.x + size, y: loc.y, z: loc.z };
|
|
262
|
+
return cleanObject(await tools.debugTools.drawDebugArrow({ start: [loc.x, loc.y, loc.z], end: [end.x, end.y, end.z], color, duration }));
|
|
263
|
+
}
|
|
264
|
+
else if (shape === 'point') {
|
|
265
|
+
return cleanObject(await tools.debugTools.drawDebugPoint({ location: [loc.x, loc.y, loc.z], size, color, duration }));
|
|
266
|
+
}
|
|
267
|
+
else if (shape === 'text' || shape === 'string') {
|
|
268
|
+
const text = args.text || 'Debug';
|
|
269
|
+
return cleanObject(await tools.debugTools.drawDebugString({ location: [loc.x, loc.y, loc.z], text, color, duration }));
|
|
270
|
+
}
|
|
271
|
+
// Default fallback
|
|
272
|
+
return cleanObject(await tools.debugTools.drawDebugSphere({ center: [loc.x, loc.y, loc.z], radius: size, color, duration }));
|
|
273
|
+
}
|
|
532
274
|
default:
|
|
533
275
|
throw new Error(`Unknown effect action: ${args.action}`);
|
|
534
276
|
}
|
|
535
|
-
break;
|
|
536
277
|
// 7. BLUEPRINT MANAGER
|
|
537
278
|
case 'manage_blueprint':
|
|
538
279
|
switch (args.action) {
|
|
539
|
-
case 'create':
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
case 'add_component':
|
|
548
|
-
mappedName = 'add_blueprint_component';
|
|
549
|
-
mappedArgs = {
|
|
550
|
-
blueprintName: args.name,
|
|
551
|
-
componentType: args.componentType,
|
|
552
|
-
componentName: args.componentName
|
|
553
|
-
};
|
|
554
|
-
break;
|
|
280
|
+
case 'create': {
|
|
281
|
+
const res = await tools.blueprintTools.createBlueprint({ name: args.name, blueprintType: args.blueprintType || 'Actor', savePath: args.savePath });
|
|
282
|
+
return cleanObject(res);
|
|
283
|
+
}
|
|
284
|
+
case 'add_component': {
|
|
285
|
+
const res = await tools.blueprintTools.addComponent({ blueprintName: args.name, componentType: args.componentType, componentName: args.componentName });
|
|
286
|
+
return cleanObject(res);
|
|
287
|
+
}
|
|
555
288
|
default:
|
|
556
289
|
throw new Error(`Unknown blueprint action: ${args.action}`);
|
|
557
290
|
}
|
|
558
|
-
break;
|
|
559
291
|
// 8. ENVIRONMENT BUILDER
|
|
560
292
|
case 'build_environment':
|
|
561
293
|
switch (args.action) {
|
|
562
|
-
case 'create_landscape':
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
294
|
+
case 'create_landscape': {
|
|
295
|
+
const res = await tools.landscapeTools.createLandscape({ name: args.name, sizeX: args.sizeX, sizeY: args.sizeY, materialPath: args.materialPath });
|
|
296
|
+
return cleanObject(res);
|
|
297
|
+
}
|
|
298
|
+
case 'sculpt': {
|
|
299
|
+
const res = await tools.landscapeTools.sculptLandscape({ landscapeName: args.name, tool: args.tool, brushSize: args.brushSize, strength: args.strength });
|
|
300
|
+
return cleanObject(res);
|
|
301
|
+
}
|
|
302
|
+
case 'add_foliage': {
|
|
303
|
+
const res = await tools.foliageTools.addFoliageType({ name: args.name, meshPath: args.meshPath, density: args.density });
|
|
304
|
+
return cleanObject(res);
|
|
305
|
+
}
|
|
306
|
+
case 'paint_foliage': {
|
|
307
|
+
const pos = args.position ? [args.position.x || 0, args.position.y || 0, args.position.z || 0] : [0, 0, 0];
|
|
308
|
+
const res = await tools.foliageTools.paintFoliage({ foliageType: args.foliageType, position: pos, brushSize: args.brushSize, paintDensity: args.paintDensity, eraseMode: args.eraseMode });
|
|
309
|
+
return cleanObject(res);
|
|
310
|
+
}
|
|
311
|
+
case 'create_procedural_terrain': {
|
|
312
|
+
const loc = args.location ? [args.location.x || 0, args.location.y || 0, args.location.z || 0] : [0, 0, 0];
|
|
313
|
+
const res = await tools.buildEnvAdvanced.createProceduralTerrain({
|
|
314
|
+
name: args.name || 'ProceduralTerrain',
|
|
315
|
+
location: loc,
|
|
566
316
|
sizeX: args.sizeX,
|
|
567
317
|
sizeY: args.sizeY,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
318
|
+
subdivisions: args.subdivisions,
|
|
319
|
+
heightFunction: args.heightFunction,
|
|
320
|
+
material: args.materialPath
|
|
321
|
+
});
|
|
322
|
+
return cleanObject(res);
|
|
323
|
+
}
|
|
324
|
+
case 'create_procedural_foliage': {
|
|
325
|
+
if (!args.bounds || !args.bounds.location || !args.bounds.size)
|
|
326
|
+
throw new Error('bounds.location and bounds.size are required');
|
|
327
|
+
const bounds = {
|
|
328
|
+
location: [args.bounds.location.x || 0, args.bounds.location.y || 0, args.bounds.location.z || 0],
|
|
329
|
+
size: [args.bounds.size.x || 1000, args.bounds.size.y || 1000, args.bounds.size.z || 100]
|
|
330
|
+
};
|
|
331
|
+
const res = await tools.buildEnvAdvanced.createProceduralFoliage({
|
|
332
|
+
name: args.name || 'ProceduralFoliage',
|
|
333
|
+
bounds,
|
|
334
|
+
foliageTypes: args.foliageTypes || [],
|
|
335
|
+
seed: args.seed
|
|
336
|
+
});
|
|
337
|
+
return cleanObject(res);
|
|
338
|
+
}
|
|
339
|
+
case 'add_foliage_instances': {
|
|
340
|
+
if (!args.foliageType)
|
|
341
|
+
throw new Error('foliageType is required');
|
|
342
|
+
if (!Array.isArray(args.transforms))
|
|
343
|
+
throw new Error('transforms array is required');
|
|
344
|
+
const transforms = args.transforms.map(t => ({
|
|
345
|
+
location: [t.location?.x || 0, t.location?.y || 0, t.location?.z || 0],
|
|
346
|
+
rotation: t.rotation ? [t.rotation.pitch || 0, t.rotation.yaw || 0, t.rotation.roll || 0] : undefined,
|
|
347
|
+
scale: t.scale ? [t.scale.x || 1, t.scale.y || 1, t.scale.z || 1] : undefined
|
|
348
|
+
}));
|
|
349
|
+
const res = await tools.buildEnvAdvanced.addFoliageInstances({ foliageType: args.foliageType, transforms });
|
|
350
|
+
return cleanObject(res);
|
|
351
|
+
}
|
|
352
|
+
case 'create_landscape_grass_type': {
|
|
353
|
+
const res = await tools.buildEnvAdvanced.createLandscapeGrassType({
|
|
354
|
+
name: args.name || 'GrassType',
|
|
596
355
|
meshPath: args.meshPath,
|
|
597
|
-
density: args.density
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
throw new Error(`Invalid foliageType: '${args.foliageType}'`);
|
|
604
|
-
}
|
|
605
|
-
// Convert position object to array if needed
|
|
606
|
-
let positionArray;
|
|
607
|
-
if (args.position) {
|
|
608
|
-
if (Array.isArray(args.position)) {
|
|
609
|
-
positionArray = args.position;
|
|
610
|
-
}
|
|
611
|
-
else if (typeof args.position === 'object') {
|
|
612
|
-
positionArray = [args.position.x || 0, args.position.y || 0, args.position.z || 0];
|
|
613
|
-
}
|
|
614
|
-
else {
|
|
615
|
-
positionArray = [0, 0, 0];
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
positionArray = [0, 0, 0];
|
|
620
|
-
}
|
|
621
|
-
// Validate numbers in position
|
|
622
|
-
if (!Array.isArray(positionArray) || positionArray.length !== 3 || positionArray.some(v => typeof v !== 'number' || !isFinite(v))) {
|
|
623
|
-
throw new Error(`Invalid position: '${JSON.stringify(args.position)}'`);
|
|
624
|
-
}
|
|
625
|
-
if (args.brushSize !== undefined) {
|
|
626
|
-
if (typeof args.brushSize !== 'number' || !isFinite(args.brushSize) || args.brushSize < 0) {
|
|
627
|
-
throw new Error(`Invalid brushSize: '${args.brushSize}' (must be non-negative finite number)`);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
mappedName = 'paint_foliage';
|
|
631
|
-
mappedArgs = {
|
|
632
|
-
foliageType: args.foliageType,
|
|
633
|
-
position: positionArray,
|
|
634
|
-
brushSize: args.brushSize
|
|
635
|
-
};
|
|
636
|
-
break;
|
|
356
|
+
density: args.density,
|
|
357
|
+
minScale: args.minScale,
|
|
358
|
+
maxScale: args.maxScale
|
|
359
|
+
});
|
|
360
|
+
return cleanObject(res);
|
|
361
|
+
}
|
|
637
362
|
default:
|
|
638
363
|
throw new Error(`Unknown environment action: ${args.action}`);
|
|
639
364
|
}
|
|
640
|
-
break;
|
|
641
365
|
// 9. SYSTEM CONTROL
|
|
642
366
|
case 'system_control':
|
|
643
|
-
|
|
644
|
-
if (args === null || args === undefined) {
|
|
645
|
-
throw new Error('Invalid arguments: null or undefined');
|
|
646
|
-
}
|
|
647
|
-
if (typeof args !== 'object') {
|
|
648
|
-
throw new Error('Invalid arguments: must be an object');
|
|
649
|
-
}
|
|
650
|
-
// Validate action exists
|
|
651
|
-
if (!args.action) {
|
|
367
|
+
if (!args.action)
|
|
652
368
|
throw new Error('Missing required parameter: action');
|
|
653
|
-
}
|
|
654
369
|
switch (args.action) {
|
|
655
|
-
case 'profile':
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
case '
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
AntiAliasing: 'AntiAliasing',
|
|
697
|
-
PostProcessing: 'PostProcess',
|
|
698
|
-
PostProcess: 'PostProcess',
|
|
699
|
-
Shadows: 'Shadow',
|
|
700
|
-
Shadow: 'Shadow',
|
|
701
|
-
GlobalIllumination: 'GlobalIllumination',
|
|
702
|
-
Reflections: 'Reflection',
|
|
703
|
-
Reflection: 'Reflection',
|
|
704
|
-
Textures: 'Texture',
|
|
705
|
-
Texture: 'Texture',
|
|
706
|
-
Effects: 'Effects',
|
|
707
|
-
Foliage: 'Foliage',
|
|
708
|
-
Shading: 'Shading',
|
|
709
|
-
};
|
|
710
|
-
const categoryName = map[String(args.category)] || args.category;
|
|
711
|
-
mappedName = 'set_scalability';
|
|
712
|
-
mappedArgs = {
|
|
713
|
-
category: categoryName,
|
|
714
|
-
level: args.level
|
|
715
|
-
};
|
|
716
|
-
break;
|
|
717
|
-
case 'play_sound':
|
|
718
|
-
// Validate sound path
|
|
719
|
-
if (!args.soundPath || typeof args.soundPath !== 'string') {
|
|
720
|
-
throw new Error('Invalid soundPath: must be a non-empty string');
|
|
721
|
-
}
|
|
722
|
-
// Validate volume if provided
|
|
723
|
-
if (args.volume !== undefined) {
|
|
724
|
-
if (typeof args.volume !== 'number' || args.volume < 0 || args.volume > 1) {
|
|
725
|
-
throw new Error(`Invalid volume: must be 0-1, got ${args.volume}`);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
mappedName = 'play_sound';
|
|
729
|
-
mappedArgs = {
|
|
730
|
-
soundPath: args.soundPath,
|
|
731
|
-
location: args.location,
|
|
732
|
-
volume: args.volume,
|
|
733
|
-
is3D: args.is3D
|
|
734
|
-
};
|
|
735
|
-
break;
|
|
736
|
-
case 'create_widget':
|
|
737
|
-
// Validate widget name
|
|
738
|
-
if (!args.widgetName || typeof args.widgetName !== 'string' || args.widgetName.trim() === '') {
|
|
739
|
-
throw new Error('Invalid widgetName: must be a non-empty string');
|
|
740
|
-
}
|
|
741
|
-
mappedName = 'create_widget';
|
|
742
|
-
mappedArgs = {
|
|
743
|
-
name: args.widgetName,
|
|
744
|
-
type: args.widgetType,
|
|
745
|
-
savePath: args.savePath
|
|
746
|
-
};
|
|
747
|
-
break;
|
|
748
|
-
case 'show_widget':
|
|
749
|
-
// Validate widget name
|
|
750
|
-
if (!args.widgetName || typeof args.widgetName !== 'string') {
|
|
751
|
-
throw new Error('Invalid widgetName: must be a non-empty string');
|
|
752
|
-
}
|
|
753
|
-
// Validate visible is boolean (default to true if not provided)
|
|
754
|
-
const isVisible = args.visible !== undefined ? args.visible : true;
|
|
755
|
-
if (typeof isVisible !== 'boolean') {
|
|
756
|
-
throw new Error(`Invalid visible: must be boolean, got ${typeof isVisible}`);
|
|
757
|
-
}
|
|
758
|
-
mappedName = 'show_widget';
|
|
759
|
-
mappedArgs = {
|
|
760
|
-
widgetName: args.widgetName,
|
|
761
|
-
visible: isVisible
|
|
762
|
-
};
|
|
763
|
-
break;
|
|
764
|
-
case 'screenshot':
|
|
765
|
-
mappedName = 'take_screenshot';
|
|
766
|
-
mappedArgs = { resolution: args.resolution };
|
|
767
|
-
break;
|
|
768
|
-
case 'engine_start':
|
|
769
|
-
mappedName = 'launch_editor';
|
|
770
|
-
mappedArgs = { editorExe: args.editorExe, projectPath: args.projectPath };
|
|
771
|
-
break;
|
|
772
|
-
case 'engine_quit':
|
|
773
|
-
mappedName = 'quit_editor';
|
|
774
|
-
mappedArgs = {};
|
|
775
|
-
break;
|
|
370
|
+
case 'profile': {
|
|
371
|
+
const res = await tools.performanceTools.startProfiling({ type: args.profileType, duration: args.duration });
|
|
372
|
+
return cleanObject(res);
|
|
373
|
+
}
|
|
374
|
+
case 'show_fps': {
|
|
375
|
+
const res = await tools.performanceTools.showFPS({ enabled: !!args.enabled, verbose: !!args.verbose });
|
|
376
|
+
return cleanObject(res);
|
|
377
|
+
}
|
|
378
|
+
case 'set_quality': {
|
|
379
|
+
const res = await tools.performanceTools.setScalability({ category: args.category, level: args.level });
|
|
380
|
+
return cleanObject(res);
|
|
381
|
+
}
|
|
382
|
+
case 'play_sound': {
|
|
383
|
+
if (args.location && typeof args.location === 'object') {
|
|
384
|
+
const loc = [args.location.x || 0, args.location.y || 0, args.location.z || 0];
|
|
385
|
+
const res = await tools.audioTools.playSoundAtLocation({ soundPath: args.soundPath, location: loc, volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
386
|
+
return cleanObject(res);
|
|
387
|
+
}
|
|
388
|
+
const res = await tools.audioTools.playSound2D({ soundPath: args.soundPath, volume: args.volume, pitch: args.pitch, startTime: args.startTime });
|
|
389
|
+
return cleanObject(res);
|
|
390
|
+
}
|
|
391
|
+
case 'create_widget': {
|
|
392
|
+
const res = await tools.uiTools.createWidget({ name: args.widgetName, type: args.widgetType, savePath: args.savePath });
|
|
393
|
+
return cleanObject(res);
|
|
394
|
+
}
|
|
395
|
+
case 'show_widget': {
|
|
396
|
+
const res = await tools.uiTools.setWidgetVisibility({ widgetName: args.widgetName, visible: args.visible !== false });
|
|
397
|
+
return cleanObject(res);
|
|
398
|
+
}
|
|
399
|
+
case 'screenshot': {
|
|
400
|
+
const res = await tools.visualTools.takeScreenshot({ resolution: args.resolution });
|
|
401
|
+
return cleanObject(res);
|
|
402
|
+
}
|
|
403
|
+
case 'engine_start': {
|
|
404
|
+
const res = await tools.engineTools.launchEditor({ editorExe: args.editorExe, projectPath: args.projectPath });
|
|
405
|
+
return cleanObject(res);
|
|
406
|
+
}
|
|
407
|
+
case 'engine_quit': {
|
|
408
|
+
const res = await tools.engineTools.quitEditor();
|
|
409
|
+
return cleanObject(res);
|
|
410
|
+
}
|
|
776
411
|
default:
|
|
777
412
|
throw new Error(`Unknown system action: ${args.action}`);
|
|
778
413
|
}
|
|
779
|
-
break;
|
|
780
414
|
// 10. CONSOLE COMMAND - handle validation here
|
|
781
415
|
case 'console_command':
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
success: true,
|
|
791
|
-
message: 'Empty command'
|
|
792
|
-
};
|
|
416
|
+
if (!args.command || typeof args.command !== 'string' || args.command.trim() === '') {
|
|
417
|
+
return { success: true, message: 'Empty command' };
|
|
418
|
+
}
|
|
419
|
+
// Basic safety filter
|
|
420
|
+
const cmd = String(args.command).trim();
|
|
421
|
+
const blocked = [/\bquit\b/i, /\bexit\b/i, /debugcrash/i];
|
|
422
|
+
if (blocked.some(r => r.test(cmd))) {
|
|
423
|
+
return { success: false, error: 'Command blocked for safety' };
|
|
793
424
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
return {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}],
|
|
801
|
-
isError: false,
|
|
802
|
-
success: true,
|
|
803
|
-
message: 'Empty command'
|
|
804
|
-
};
|
|
425
|
+
try {
|
|
426
|
+
const res = await tools.bridge.executeConsoleCommand(cmd);
|
|
427
|
+
return cleanObject({ success: true, command: cmd, result: res });
|
|
428
|
+
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
return cleanObject({ success: false, command: cmd, error: e?.message || String(e) });
|
|
805
431
|
}
|
|
806
|
-
mappedName = 'console_command';
|
|
807
|
-
mappedArgs = args;
|
|
808
|
-
break;
|
|
809
432
|
// 11. REMOTE CONTROL PRESETS - Direct implementation
|
|
810
433
|
case 'manage_rc':
|
|
811
434
|
if (!args.action)
|
|
@@ -1004,34 +627,21 @@ export async function handleConsolidatedToolCall(name, args, tools) {
|
|
|
1004
627
|
if (!args.action)
|
|
1005
628
|
throw new Error('Missing required parameter: action');
|
|
1006
629
|
switch (args.action) {
|
|
1007
|
-
case 'inspect_object':
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
case 'set_property':
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
630
|
+
case 'inspect_object': {
|
|
631
|
+
const res = await tools.introspectionTools.inspectObject({ objectPath: args.objectPath, detailed: args.detailed });
|
|
632
|
+
return cleanObject(res);
|
|
633
|
+
}
|
|
634
|
+
case 'set_property': {
|
|
635
|
+
const res = await tools.introspectionTools.setProperty({ objectPath: args.objectPath, propertyName: args.propertyName, value: args.value });
|
|
636
|
+
return cleanObject(res);
|
|
637
|
+
}
|
|
1015
638
|
default:
|
|
1016
639
|
throw new Error(`Unknown inspect action: ${args.action}`);
|
|
1017
640
|
}
|
|
1018
|
-
break;
|
|
1019
641
|
default:
|
|
1020
642
|
throw new Error(`Unknown consolidated tool: ${name}`);
|
|
1021
643
|
}
|
|
1022
|
-
//
|
|
1023
|
-
const TOOL_TIMEOUT = 15000; // 15 seconds timeout for tool execution
|
|
1024
|
-
const toolPromise = handleToolCall(mappedName, mappedArgs, tools);
|
|
1025
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
1026
|
-
setTimeout(() => {
|
|
1027
|
-
reject(new Error(`Tool execution timeout after ${TOOL_TIMEOUT}ms`));
|
|
1028
|
-
}, TOOL_TIMEOUT);
|
|
1029
|
-
});
|
|
1030
|
-
const result = await Promise.race([toolPromise, timeoutPromise]);
|
|
1031
|
-
const duration = Date.now() - startTime;
|
|
1032
|
-
console.log(`[ConsolidatedToolHandler] Completed execution of ${name} in ${duration}ms`);
|
|
1033
|
-
// Clean the result to prevent circular reference errors
|
|
1034
|
-
return cleanObject(result);
|
|
644
|
+
// All cases return (or throw) above; this is a type guard for exhaustiveness.
|
|
1035
645
|
}
|
|
1036
646
|
catch (err) {
|
|
1037
647
|
const duration = Date.now() - startTime;
|