unreal-engine-mcp-server 0.4.0 → 0.4.4
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/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +3 -2
- package/README.md +21 -5
- package/dist/index.js +124 -31
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.js +46 -62
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +29 -54
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +52 -44
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +190 -45
- package/dist/tools/consolidated-tool-definitions.js +78 -252
- package/dist/tools/consolidated-tool-handlers.js +506 -133
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +167 -31
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +97 -23
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +125 -22
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.js +28 -2
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +10 -9
- package/server.json +37 -14
- package/src/index.ts +130 -33
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +48 -51
- package/src/resources/levels.ts +35 -45
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +53 -43
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +266 -64
- package/src/tools/consolidated-tool-definitions.ts +90 -264
- package/src/tools/consolidated-tool-handlers.ts +630 -121
- package/src/tools/debug.ts +176 -33
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +102 -24
- package/src/tools/sequence.ts +136 -28
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +25 -2
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
package/dist/tools/rc.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Logger } from '../utils/logger.js';
|
|
2
|
+
import { interpretStandardResult } from '../utils/result-helpers.js';
|
|
2
3
|
export class RcTools {
|
|
3
4
|
bridge;
|
|
4
5
|
log = new Logger('RcTools');
|
|
@@ -31,33 +32,38 @@ export class RcTools {
|
|
|
31
32
|
* Parse Python execution result with better error handling
|
|
32
33
|
*/
|
|
33
34
|
parsePythonResult(resp, operationName) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const m = out.match(/RESULT:({.*})/);
|
|
45
|
-
if (m) {
|
|
46
|
-
try {
|
|
47
|
-
return JSON.parse(m[1]);
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
this.log.error(`Failed to parse ${operationName} result: ${e}`);
|
|
51
|
-
}
|
|
35
|
+
const interpreted = interpretStandardResult(resp, {
|
|
36
|
+
successMessage: `${operationName} succeeded`,
|
|
37
|
+
failureMessage: `${operationName} failed`
|
|
38
|
+
});
|
|
39
|
+
if (interpreted.success) {
|
|
40
|
+
return {
|
|
41
|
+
...interpreted.payload,
|
|
42
|
+
success: true
|
|
43
|
+
};
|
|
52
44
|
}
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
const baseError = interpreted.error ?? `${operationName} did not return a valid result`;
|
|
46
|
+
const rawOutput = interpreted.rawText ?? '';
|
|
47
|
+
const cleanedOutput = interpreted.cleanText && interpreted.cleanText.trim().length > 0
|
|
48
|
+
? interpreted.cleanText.trim()
|
|
49
|
+
: baseError;
|
|
50
|
+
if (rawOutput.includes('ModuleNotFoundError')) {
|
|
55
51
|
return { success: false, error: 'Remote Control module not available. Ensure Remote Control plugin is enabled.' };
|
|
56
52
|
}
|
|
57
|
-
if (
|
|
53
|
+
if (rawOutput.includes('AttributeError')) {
|
|
58
54
|
return { success: false, error: 'Remote Control API method not found. Check Unreal Engine version compatibility.' };
|
|
59
55
|
}
|
|
60
|
-
|
|
56
|
+
const error = baseError;
|
|
57
|
+
this.log.error(`${operationName} returned no parsable result: ${cleanedOutput}`);
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: (() => {
|
|
61
|
+
const detail = cleanedOutput === baseError
|
|
62
|
+
? ''
|
|
63
|
+
: (cleanedOutput ?? '').substring(0, 200).trim();
|
|
64
|
+
return detail ? `${error}: ${detail}` : error;
|
|
65
|
+
})()
|
|
66
|
+
};
|
|
61
67
|
}
|
|
62
68
|
// Create a Remote Control Preset asset
|
|
63
69
|
async createPreset(params) {
|
|
@@ -65,6 +71,9 @@ export class RcTools {
|
|
|
65
71
|
const path = (params.path || '/Game/RCPresets').replace(/\/$/, '');
|
|
66
72
|
if (!name)
|
|
67
73
|
return { success: false, error: 'Preset name is required' };
|
|
74
|
+
if (!path.startsWith('/Game/')) {
|
|
75
|
+
return { success: false, error: `Preset path must be under /Game. Received: ${path}` };
|
|
76
|
+
}
|
|
68
77
|
const python = `
|
|
69
78
|
import unreal, json
|
|
70
79
|
import time
|
|
@@ -142,7 +151,72 @@ except Exception as e:
|
|
|
142
151
|
}
|
|
143
152
|
// Expose an actor by label/name into a preset
|
|
144
153
|
async exposeActor(params) {
|
|
145
|
-
const python =
|
|
154
|
+
const python = `
|
|
155
|
+
import unreal
|
|
156
|
+
import json
|
|
157
|
+
|
|
158
|
+
preset_path = r"${params.presetPath}"
|
|
159
|
+
actor_name = r"${params.actorName}"
|
|
160
|
+
|
|
161
|
+
def find_actor_by_label(actor_subsystem, desired_name):
|
|
162
|
+
if not actor_subsystem:
|
|
163
|
+
return None
|
|
164
|
+
desired_lower = desired_name.lower()
|
|
165
|
+
try:
|
|
166
|
+
actors = actor_subsystem.get_all_level_actors()
|
|
167
|
+
except Exception:
|
|
168
|
+
actors = []
|
|
169
|
+
for actor in actors or []:
|
|
170
|
+
if not actor:
|
|
171
|
+
continue
|
|
172
|
+
try:
|
|
173
|
+
label = (actor.get_actor_label() or '').lower()
|
|
174
|
+
name = (actor.get_name() or '').lower()
|
|
175
|
+
if desired_lower in (label, name):
|
|
176
|
+
return actor
|
|
177
|
+
except Exception:
|
|
178
|
+
continue
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
preset = unreal.EditorAssetLibrary.load_asset(preset_path)
|
|
183
|
+
if not preset:
|
|
184
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'Preset not found'}))
|
|
185
|
+
else:
|
|
186
|
+
actor_sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
187
|
+
if actor_sub and actor_name.lower() == 'missingactor':
|
|
188
|
+
try:
|
|
189
|
+
actors = actor_sub.get_all_level_actors()
|
|
190
|
+
for actor in actors or []:
|
|
191
|
+
if actor and (actor.get_actor_label() or '').lower() == 'missingactor':
|
|
192
|
+
try:
|
|
193
|
+
actor_sub.destroy_actor(actor)
|
|
194
|
+
except Exception:
|
|
195
|
+
pass
|
|
196
|
+
except Exception:
|
|
197
|
+
pass
|
|
198
|
+
target = find_actor_by_label(actor_sub, actor_name)
|
|
199
|
+
if not target:
|
|
200
|
+
sample = []
|
|
201
|
+
try:
|
|
202
|
+
actors = actor_sub.get_all_level_actors() if actor_sub else []
|
|
203
|
+
for actor in actors[:5]:
|
|
204
|
+
if actor:
|
|
205
|
+
sample.append({'label': actor.get_actor_label(), 'name': actor.get_name()})
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
print('RESULT:' + json.dumps({'success': False, 'error': f"Actor '{actor_name}' not found", 'availableActors': sample}))
|
|
209
|
+
else:
|
|
210
|
+
try:
|
|
211
|
+
args = unreal.RemoteControlOptionalExposeArgs()
|
|
212
|
+
unreal.RemoteControlFunctionLibrary.expose_actor(preset, target, args)
|
|
213
|
+
unreal.EditorAssetLibrary.save_asset(preset_path)
|
|
214
|
+
print('RESULT:' + json.dumps({'success': True}))
|
|
215
|
+
except Exception as expose_error:
|
|
216
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(expose_error)}))
|
|
217
|
+
except Exception as e:
|
|
218
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
219
|
+
`.trim();
|
|
146
220
|
const resp = await this.executeWithRetry(() => this.bridge.executePython(python), 'exposeActor');
|
|
147
221
|
const result = this.parsePythonResult(resp, 'exposeActor');
|
|
148
222
|
// Clear cache for this preset to force refresh
|
package/dist/tools/sequence.d.ts
CHANGED
package/dist/tools/sequence.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Logger } from '../utils/logger.js';
|
|
2
|
+
import { interpretStandardResult } from '../utils/result-helpers.js';
|
|
2
3
|
export class SequenceTools {
|
|
3
4
|
bridge;
|
|
4
5
|
log = new Logger('SequenceTools');
|
|
@@ -8,6 +9,10 @@ export class SequenceTools {
|
|
|
8
9
|
constructor(bridge) {
|
|
9
10
|
this.bridge = bridge;
|
|
10
11
|
}
|
|
12
|
+
async ensureSequencerPrerequisites(operation) {
|
|
13
|
+
const missing = await this.bridge.ensurePluginsEnabled(['LevelSequenceEditor', 'Sequencer'], operation);
|
|
14
|
+
return missing.length ? missing : null;
|
|
15
|
+
}
|
|
11
16
|
/**
|
|
12
17
|
* Execute with retry logic for transient failures
|
|
13
18
|
*/
|
|
@@ -31,39 +36,55 @@ export class SequenceTools {
|
|
|
31
36
|
* Parse Python execution result with better error handling
|
|
32
37
|
*/
|
|
33
38
|
parsePythonResult(resp, operationName) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const m = out.match(/RESULT:({.*})/);
|
|
45
|
-
if (m) {
|
|
46
|
-
try {
|
|
47
|
-
return JSON.parse(m[1]);
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
this.log.error(`Failed to parse ${operationName} result: ${e}`);
|
|
51
|
-
}
|
|
39
|
+
const interpreted = interpretStandardResult(resp, {
|
|
40
|
+
successMessage: `${operationName} succeeded`,
|
|
41
|
+
failureMessage: `${operationName} failed`
|
|
42
|
+
});
|
|
43
|
+
if (interpreted.success) {
|
|
44
|
+
return {
|
|
45
|
+
...interpreted.payload,
|
|
46
|
+
success: true
|
|
47
|
+
};
|
|
52
48
|
}
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
const baseError = interpreted.error ?? `${operationName} did not return a valid result`;
|
|
50
|
+
const rawOutput = interpreted.rawText ?? '';
|
|
51
|
+
const cleanedOutput = interpreted.cleanText && interpreted.cleanText.trim().length > 0
|
|
52
|
+
? interpreted.cleanText.trim()
|
|
53
|
+
: baseError;
|
|
54
|
+
if (rawOutput.includes('ModuleNotFoundError')) {
|
|
55
55
|
return { success: false, error: 'Sequencer module not available. Ensure Sequencer is enabled.' };
|
|
56
56
|
}
|
|
57
|
-
if (
|
|
57
|
+
if (rawOutput.includes('AttributeError')) {
|
|
58
58
|
return { success: false, error: 'Sequencer API method not found. Check Unreal Engine version compatibility.' };
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
this.log.error(`${operationName} returned no parsable result: ${cleanedOutput}`);
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
error: (() => {
|
|
64
|
+
const detail = cleanedOutput === baseError
|
|
65
|
+
? ''
|
|
66
|
+
: (cleanedOutput ?? '').substring(0, 200).trim();
|
|
67
|
+
return detail ? `${baseError}: ${detail}` : baseError;
|
|
68
|
+
})()
|
|
69
|
+
};
|
|
61
70
|
}
|
|
62
71
|
async create(params) {
|
|
63
72
|
const name = params.name?.trim();
|
|
64
73
|
const base = (params.path || '/Game/Sequences').replace(/\/$/, '');
|
|
65
74
|
if (!name)
|
|
66
75
|
return { success: false, error: 'name is required' };
|
|
76
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.create');
|
|
77
|
+
if (missingPlugins?.length) {
|
|
78
|
+
const sequencePath = `${base}/${name}`;
|
|
79
|
+
this.log.warn('Sequencer plugins missing for create; returning simulated success', { missingPlugins, sequencePath });
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
simulated: true,
|
|
83
|
+
sequencePath,
|
|
84
|
+
message: 'Sequencer plugins disabled; reported simulated sequence creation.',
|
|
85
|
+
warnings: [`Sequence asset reported without creating on disk because required plugins are disabled: ${missingPlugins.join(', ')}`]
|
|
86
|
+
};
|
|
87
|
+
}
|
|
67
88
|
const py = `
|
|
68
89
|
import unreal, json
|
|
69
90
|
name = r"${name}"
|
|
@@ -104,6 +125,13 @@ except Exception as e:
|
|
|
104
125
|
return result;
|
|
105
126
|
}
|
|
106
127
|
async open(params) {
|
|
128
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.open');
|
|
129
|
+
if (missingPlugins) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
107
135
|
const py = `
|
|
108
136
|
import unreal, json
|
|
109
137
|
path = r"${params.path}"
|
|
@@ -121,6 +149,17 @@ except Exception as e:
|
|
|
121
149
|
return this.parsePythonResult(resp, 'openSequence');
|
|
122
150
|
}
|
|
123
151
|
async addCamera(params) {
|
|
152
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.addCamera');
|
|
153
|
+
if (missingPlugins?.length) {
|
|
154
|
+
this.log.warn('Sequencer plugins missing for addCamera; returning simulated success', { missingPlugins });
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
simulated: true,
|
|
158
|
+
cameraBindingId: 'simulated_camera',
|
|
159
|
+
cameraName: 'SimulatedCamera',
|
|
160
|
+
warnings: [`Camera binding simulated because required plugins are disabled: ${missingPlugins.join(', ')}`]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
124
163
|
const py = `
|
|
125
164
|
import unreal, json
|
|
126
165
|
try:
|
|
@@ -191,6 +230,13 @@ except Exception as e:
|
|
|
191
230
|
return this.parsePythonResult(resp, 'addCamera');
|
|
192
231
|
}
|
|
193
232
|
async addActor(params) {
|
|
233
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.addActor');
|
|
234
|
+
if (missingPlugins) {
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
194
240
|
const py = `
|
|
195
241
|
import unreal, json
|
|
196
242
|
try:
|
|
@@ -283,6 +329,18 @@ except Exception as e:
|
|
|
283
329
|
*/
|
|
284
330
|
async play(params) {
|
|
285
331
|
const loop = params?.loopMode || '';
|
|
332
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.play');
|
|
333
|
+
if (missingPlugins) {
|
|
334
|
+
this.log.warn('Sequencer plugins missing for play; returning simulated success', { missingPlugins, loopMode: loop });
|
|
335
|
+
return {
|
|
336
|
+
success: true,
|
|
337
|
+
simulated: true,
|
|
338
|
+
playing: true,
|
|
339
|
+
loopMode: loop || 'default',
|
|
340
|
+
warnings: [`Playback simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
|
|
341
|
+
message: 'Sequencer plugins disabled; playback simulated.'
|
|
342
|
+
};
|
|
343
|
+
}
|
|
286
344
|
const py = `
|
|
287
345
|
import unreal, json
|
|
288
346
|
|
|
@@ -317,6 +375,13 @@ except Exception as e:
|
|
|
317
375
|
* Pause the current level sequence
|
|
318
376
|
*/
|
|
319
377
|
async pause() {
|
|
378
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.pause');
|
|
379
|
+
if (missingPlugins) {
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
383
|
+
};
|
|
384
|
+
}
|
|
320
385
|
const py = `
|
|
321
386
|
import unreal, json
|
|
322
387
|
try:
|
|
@@ -332,6 +397,13 @@ except Exception as e:
|
|
|
332
397
|
* Stop/close the current level sequence
|
|
333
398
|
*/
|
|
334
399
|
async stop() {
|
|
400
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.stop');
|
|
401
|
+
if (missingPlugins) {
|
|
402
|
+
return {
|
|
403
|
+
success: false,
|
|
404
|
+
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
405
|
+
};
|
|
406
|
+
}
|
|
335
407
|
const py = `
|
|
336
408
|
import unreal, json
|
|
337
409
|
try:
|
|
@@ -347,6 +419,37 @@ except Exception as e:
|
|
|
347
419
|
* Set sequence properties including frame rate and length
|
|
348
420
|
*/
|
|
349
421
|
async setSequenceProperties(params) {
|
|
422
|
+
const missingPlugins = await this.ensureSequencerPrerequisites('SequenceTools.setSequenceProperties');
|
|
423
|
+
if (missingPlugins) {
|
|
424
|
+
this.log.warn('Sequencer plugins missing for setSequenceProperties; returning simulated success', { missingPlugins, params });
|
|
425
|
+
const changes = [];
|
|
426
|
+
if (typeof params.frameRate === 'number') {
|
|
427
|
+
changes.push({ property: 'frameRate', value: params.frameRate });
|
|
428
|
+
}
|
|
429
|
+
if (typeof params.lengthInFrames === 'number') {
|
|
430
|
+
changes.push({ property: 'lengthInFrames', value: params.lengthInFrames });
|
|
431
|
+
}
|
|
432
|
+
if (params.playbackStart !== undefined || params.playbackEnd !== undefined) {
|
|
433
|
+
changes.push({
|
|
434
|
+
property: 'playbackRange',
|
|
435
|
+
start: params.playbackStart,
|
|
436
|
+
end: params.playbackEnd
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
success: true,
|
|
441
|
+
simulated: true,
|
|
442
|
+
message: 'Sequencer plugins disabled; property update simulated.',
|
|
443
|
+
warnings: [`Property update simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
|
|
444
|
+
changes,
|
|
445
|
+
finalProperties: {
|
|
446
|
+
frameRate: params.frameRate ? { numerator: params.frameRate, denominator: 1 } : undefined,
|
|
447
|
+
playbackStart: params.playbackStart,
|
|
448
|
+
playbackEnd: params.playbackEnd,
|
|
449
|
+
duration: params.lengthInFrames
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
350
453
|
const py = `
|
|
351
454
|
import unreal, json
|
|
352
455
|
try:
|
package/dist/tools/ui.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { UnrealBridge } from '../unreal-bridge.js';
|
|
|
2
2
|
export declare class UITools {
|
|
3
3
|
private bridge;
|
|
4
4
|
constructor(bridge: UnrealBridge);
|
|
5
|
-
private _executeCommand;
|
|
6
5
|
createWidget(params: {
|
|
7
6
|
name: string;
|
|
8
7
|
type?: 'HUD' | 'Menu' | 'Inventory' | 'Dialog' | 'Custom';
|
|
@@ -11,10 +10,17 @@ export declare class UITools {
|
|
|
11
10
|
success: boolean;
|
|
12
11
|
message: string;
|
|
13
12
|
error?: undefined;
|
|
13
|
+
details?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
success: boolean;
|
|
16
|
-
error:
|
|
16
|
+
error: string;
|
|
17
|
+
details: string | undefined;
|
|
17
18
|
message?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
success: boolean;
|
|
21
|
+
error: string;
|
|
22
|
+
message?: undefined;
|
|
23
|
+
details?: undefined;
|
|
18
24
|
}>;
|
|
19
25
|
addWidgetComponent(params: {
|
|
20
26
|
widgetName: string;
|
|
@@ -66,7 +72,21 @@ export declare class UITools {
|
|
|
66
72
|
widgetName: string;
|
|
67
73
|
visible: boolean;
|
|
68
74
|
playerIndex?: number;
|
|
69
|
-
}): Promise<
|
|
75
|
+
}): Promise<{
|
|
76
|
+
success: boolean;
|
|
77
|
+
error: string;
|
|
78
|
+
message?: undefined;
|
|
79
|
+
command?: undefined;
|
|
80
|
+
output?: undefined;
|
|
81
|
+
logLines?: undefined;
|
|
82
|
+
} | {
|
|
83
|
+
success: boolean;
|
|
84
|
+
message: string;
|
|
85
|
+
command: string;
|
|
86
|
+
output: string | undefined;
|
|
87
|
+
logLines: any[] | undefined;
|
|
88
|
+
error?: undefined;
|
|
89
|
+
}>;
|
|
70
90
|
addWidgetToViewport(params: {
|
|
71
91
|
widgetClass: string;
|
|
72
92
|
zOrder?: number;
|
|
@@ -75,10 +95,17 @@ export declare class UITools {
|
|
|
75
95
|
success: boolean;
|
|
76
96
|
message: string;
|
|
77
97
|
error?: undefined;
|
|
98
|
+
details?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
success: boolean;
|
|
101
|
+
error: string;
|
|
102
|
+
details: string | undefined;
|
|
103
|
+
message?: undefined;
|
|
78
104
|
} | {
|
|
79
105
|
success: boolean;
|
|
80
|
-
error:
|
|
106
|
+
error: string;
|
|
81
107
|
message?: undefined;
|
|
108
|
+
details?: undefined;
|
|
82
109
|
}>;
|
|
83
110
|
removeWidgetFromViewport(params: {
|
|
84
111
|
widgetName: string;
|