unreal-engine-mcp-server 0.2.1
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/.dockerignore +57 -0
- package/.env.production +25 -0
- package/.eslintrc.json +54 -0
- package/.github/workflows/publish-mcp.yml +75 -0
- package/Dockerfile +54 -0
- package/LICENSE +21 -0
- package/Public/icon.png +0 -0
- package/README.md +209 -0
- package/claude_desktop_config_example.json +13 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +7 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +484 -0
- package/dist/prompts/index.d.ts +14 -0
- package/dist/prompts/index.js +38 -0
- package/dist/python-utils.d.ts +29 -0
- package/dist/python-utils.js +54 -0
- package/dist/resources/actors.d.ts +13 -0
- package/dist/resources/actors.js +83 -0
- package/dist/resources/assets.d.ts +23 -0
- package/dist/resources/assets.js +245 -0
- package/dist/resources/levels.d.ts +17 -0
- package/dist/resources/levels.js +94 -0
- package/dist/tools/actors.d.ts +51 -0
- package/dist/tools/actors.js +459 -0
- package/dist/tools/animation.d.ts +196 -0
- package/dist/tools/animation.js +579 -0
- package/dist/tools/assets.d.ts +21 -0
- package/dist/tools/assets.js +304 -0
- package/dist/tools/audio.d.ts +170 -0
- package/dist/tools/audio.js +416 -0
- package/dist/tools/blueprint.d.ts +144 -0
- package/dist/tools/blueprint.js +652 -0
- package/dist/tools/build_environment_advanced.d.ts +66 -0
- package/dist/tools/build_environment_advanced.js +484 -0
- package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
- package/dist/tools/consolidated-tool-definitions.js +607 -0
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
- package/dist/tools/consolidated-tool-handlers.js +1050 -0
- package/dist/tools/debug.d.ts +185 -0
- package/dist/tools/debug.js +265 -0
- package/dist/tools/editor.d.ts +88 -0
- package/dist/tools/editor.js +365 -0
- package/dist/tools/engine.d.ts +30 -0
- package/dist/tools/engine.js +36 -0
- package/dist/tools/foliage.d.ts +155 -0
- package/dist/tools/foliage.js +525 -0
- package/dist/tools/introspection.d.ts +98 -0
- package/dist/tools/introspection.js +683 -0
- package/dist/tools/landscape.d.ts +158 -0
- package/dist/tools/landscape.js +375 -0
- package/dist/tools/level.d.ts +110 -0
- package/dist/tools/level.js +362 -0
- package/dist/tools/lighting.d.ts +159 -0
- package/dist/tools/lighting.js +1179 -0
- package/dist/tools/materials.d.ts +34 -0
- package/dist/tools/materials.js +146 -0
- package/dist/tools/niagara.d.ts +145 -0
- package/dist/tools/niagara.js +289 -0
- package/dist/tools/performance.d.ts +163 -0
- package/dist/tools/performance.js +412 -0
- package/dist/tools/physics.d.ts +189 -0
- package/dist/tools/physics.js +784 -0
- package/dist/tools/rc.d.ts +110 -0
- package/dist/tools/rc.js +363 -0
- package/dist/tools/sequence.d.ts +112 -0
- package/dist/tools/sequence.js +675 -0
- package/dist/tools/tool-definitions.d.ts +4919 -0
- package/dist/tools/tool-definitions.js +891 -0
- package/dist/tools/tool-handlers.d.ts +47 -0
- package/dist/tools/tool-handlers.js +830 -0
- package/dist/tools/ui.d.ts +171 -0
- package/dist/tools/ui.js +337 -0
- package/dist/tools/visual.d.ts +29 -0
- package/dist/tools/visual.js +67 -0
- package/dist/types/env.d.ts +10 -0
- package/dist/types/env.js +18 -0
- package/dist/types/index.d.ts +323 -0
- package/dist/types/index.js +28 -0
- package/dist/types/tool-types.d.ts +274 -0
- package/dist/types/tool-types.js +13 -0
- package/dist/unreal-bridge.d.ts +126 -0
- package/dist/unreal-bridge.js +992 -0
- package/dist/utils/cache-manager.d.ts +64 -0
- package/dist/utils/cache-manager.js +176 -0
- package/dist/utils/error-handler.d.ts +66 -0
- package/dist/utils/error-handler.js +243 -0
- package/dist/utils/errors.d.ts +133 -0
- package/dist/utils/errors.js +256 -0
- package/dist/utils/http.d.ts +26 -0
- package/dist/utils/http.js +135 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +32 -0
- package/dist/utils/normalize.d.ts +17 -0
- package/dist/utils/normalize.js +49 -0
- package/dist/utils/response-validator.d.ts +34 -0
- package/dist/utils/response-validator.js +121 -0
- package/dist/utils/safe-json.d.ts +4 -0
- package/dist/utils/safe-json.js +97 -0
- package/dist/utils/stdio-redirect.d.ts +2 -0
- package/dist/utils/stdio-redirect.js +20 -0
- package/dist/utils/validation.d.ts +50 -0
- package/dist/utils/validation.js +173 -0
- package/mcp-config-example.json +14 -0
- package/package.json +63 -0
- package/server.json +60 -0
- package/src/cli.ts +7 -0
- package/src/index.ts +543 -0
- package/src/prompts/index.ts +51 -0
- package/src/python/editor_compat.py +181 -0
- package/src/python-utils.ts +57 -0
- package/src/resources/actors.ts +92 -0
- package/src/resources/assets.ts +251 -0
- package/src/resources/levels.ts +83 -0
- package/src/tools/actors.ts +480 -0
- package/src/tools/animation.ts +713 -0
- package/src/tools/assets.ts +305 -0
- package/src/tools/audio.ts +548 -0
- package/src/tools/blueprint.ts +736 -0
- package/src/tools/build_environment_advanced.ts +526 -0
- package/src/tools/consolidated-tool-definitions.ts +619 -0
- package/src/tools/consolidated-tool-handlers.ts +1093 -0
- package/src/tools/debug.ts +368 -0
- package/src/tools/editor.ts +360 -0
- package/src/tools/engine.ts +32 -0
- package/src/tools/foliage.ts +652 -0
- package/src/tools/introspection.ts +778 -0
- package/src/tools/landscape.ts +523 -0
- package/src/tools/level.ts +410 -0
- package/src/tools/lighting.ts +1316 -0
- package/src/tools/materials.ts +148 -0
- package/src/tools/niagara.ts +312 -0
- package/src/tools/performance.ts +549 -0
- package/src/tools/physics.ts +924 -0
- package/src/tools/rc.ts +437 -0
- package/src/tools/sequence.ts +791 -0
- package/src/tools/tool-definitions.ts +907 -0
- package/src/tools/tool-handlers.ts +941 -0
- package/src/tools/ui.ts +499 -0
- package/src/tools/visual.ts +60 -0
- package/src/types/env.ts +27 -0
- package/src/types/index.ts +414 -0
- package/src/types/tool-types.ts +343 -0
- package/src/unreal-bridge.ts +1118 -0
- package/src/utils/cache-manager.ts +213 -0
- package/src/utils/error-handler.ts +320 -0
- package/src/utils/errors.ts +312 -0
- package/src/utils/http.ts +184 -0
- package/src/utils/logger.ts +30 -0
- package/src/utils/normalize.ts +54 -0
- package/src/utils/response-validator.ts +145 -0
- package/src/utils/safe-json.ts +112 -0
- package/src/utils/stdio-redirect.ts +18 -0
- package/src/utils/validation.ts +212 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
export class AudioTools {
|
|
2
|
+
bridge;
|
|
3
|
+
constructor(bridge) {
|
|
4
|
+
this.bridge = bridge;
|
|
5
|
+
}
|
|
6
|
+
// Execute console command
|
|
7
|
+
async _executeCommand(command) {
|
|
8
|
+
return this.bridge.httpCall('/remote/object/call', 'PUT', {
|
|
9
|
+
objectPath: '/Script/Engine.Default__KismetSystemLibrary',
|
|
10
|
+
functionName: 'ExecuteConsoleCommand',
|
|
11
|
+
parameters: {
|
|
12
|
+
WorldContextObject: null,
|
|
13
|
+
Command: command,
|
|
14
|
+
SpecificPlayer: null
|
|
15
|
+
},
|
|
16
|
+
generateTransaction: false
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
// Create sound cue
|
|
20
|
+
async createSoundCue(params) {
|
|
21
|
+
const path = params.savePath || '/Game/Audio/Cues';
|
|
22
|
+
const py = `
|
|
23
|
+
import unreal
|
|
24
|
+
import json
|
|
25
|
+
name = r"${params.name}"
|
|
26
|
+
path = r"${path}"
|
|
27
|
+
try:
|
|
28
|
+
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
|
|
29
|
+
try:
|
|
30
|
+
factory = unreal.SoundCueFactoryNew()
|
|
31
|
+
except Exception:
|
|
32
|
+
factory = None
|
|
33
|
+
if not factory:
|
|
34
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'SoundCueFactoryNew unavailable'}))
|
|
35
|
+
else:
|
|
36
|
+
asset = asset_tools.create_asset(asset_name=name, package_path=path, asset_class=unreal.SoundCue, factory=factory)
|
|
37
|
+
if asset:
|
|
38
|
+
if ${params.wavePath !== undefined ? 'True' : 'False'}:
|
|
39
|
+
try:
|
|
40
|
+
wave_path = r"${params.wavePath || ''}"
|
|
41
|
+
if wave_path and unreal.EditorAssetLibrary.does_asset_exist(wave_path):
|
|
42
|
+
snd = unreal.EditorAssetLibrary.load_asset(wave_path)
|
|
43
|
+
# Simple node hookup via SoundCueGraph is non-trivial via Python; leave as empty cue
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
unreal.EditorAssetLibrary.save_asset(f"{path}/{name}")
|
|
47
|
+
print('RESULT:' + json.dumps({'success': True}))
|
|
48
|
+
else:
|
|
49
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'Failed to create SoundCue'}))
|
|
50
|
+
except Exception as e:
|
|
51
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
52
|
+
`.trim();
|
|
53
|
+
try {
|
|
54
|
+
const resp = await this.bridge.executePython(py);
|
|
55
|
+
const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
|
|
56
|
+
const m = out.match(/RESULT:({.*})/);
|
|
57
|
+
if (m) {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(m[1]);
|
|
60
|
+
return parsed.success ? { success: true, message: 'Sound cue created' } : { success: false, error: parsed.error };
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}
|
|
64
|
+
return { success: true, message: 'Sound cue creation attempted' };
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return { success: false, error: `Failed to create sound cue: ${e}` };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Play sound at location
|
|
71
|
+
async playSoundAtLocation(params) {
|
|
72
|
+
const volume = params.volume ?? 1.0;
|
|
73
|
+
const pitch = params.pitch ?? 1.0;
|
|
74
|
+
const startTime = params.startTime ?? 0.0;
|
|
75
|
+
const py = `
|
|
76
|
+
import unreal
|
|
77
|
+
import json
|
|
78
|
+
loc = unreal.Vector(${params.location[0]}, ${params.location[1]}, ${params.location[2]})
|
|
79
|
+
path = r"${params.soundPath}"
|
|
80
|
+
try:
|
|
81
|
+
if not unreal.EditorAssetLibrary.does_asset_exist(path):
|
|
82
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'Sound asset not found'}))
|
|
83
|
+
else:
|
|
84
|
+
snd = unreal.EditorAssetLibrary.load_asset(path)
|
|
85
|
+
# Get editor world via EditorSubsystem first to avoid deprecation
|
|
86
|
+
try:
|
|
87
|
+
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
88
|
+
except Exception:
|
|
89
|
+
try:
|
|
90
|
+
world = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem).get_editor_world()
|
|
91
|
+
except Exception:
|
|
92
|
+
world = unreal.EditorLevelLibrary.get_editor_world()
|
|
93
|
+
rot = unreal.Rotator(0.0, 0.0, 0.0)
|
|
94
|
+
# Use spawn_* variant with explicit rotation before optional floats
|
|
95
|
+
unreal.GameplayStatics.spawn_sound_at_location(world, snd, loc, rot, ${volume}, ${pitch}, ${startTime})
|
|
96
|
+
print('RESULT:' + json.dumps({'success': True}))
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
99
|
+
`.trim();
|
|
100
|
+
try {
|
|
101
|
+
const resp = await this.bridge.executePython(py);
|
|
102
|
+
const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
|
|
103
|
+
const m = out.match(/RESULT:({.*})/);
|
|
104
|
+
if (m) {
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(m[1]);
|
|
107
|
+
return parsed.success ? { success: true, message: 'Sound played' } : { success: false, error: parsed.error };
|
|
108
|
+
}
|
|
109
|
+
catch { }
|
|
110
|
+
}
|
|
111
|
+
return { success: true, message: 'Sound play attempted' };
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
return { success: false, error: `Failed to play sound: ${e}` };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Play sound 2D
|
|
118
|
+
async playSound2D(params) {
|
|
119
|
+
const volume = params.volume ?? 1.0;
|
|
120
|
+
const pitch = params.pitch ?? 1.0;
|
|
121
|
+
const startTime = params.startTime ?? 0.0;
|
|
122
|
+
const py = `
|
|
123
|
+
import unreal
|
|
124
|
+
import json
|
|
125
|
+
path = r"${params.soundPath}"
|
|
126
|
+
try:
|
|
127
|
+
if not unreal.EditorAssetLibrary.does_asset_exist(path):
|
|
128
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'Sound asset not found'}))
|
|
129
|
+
else:
|
|
130
|
+
snd = unreal.EditorAssetLibrary.load_asset(path)
|
|
131
|
+
# Get editor world via EditorSubsystem first to avoid deprecation
|
|
132
|
+
try:
|
|
133
|
+
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
134
|
+
except Exception:
|
|
135
|
+
try:
|
|
136
|
+
world = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem).get_editor_world()
|
|
137
|
+
except Exception:
|
|
138
|
+
world = unreal.EditorLevelLibrary.get_editor_world()
|
|
139
|
+
ok = False
|
|
140
|
+
try:
|
|
141
|
+
unreal.GameplayStatics.spawn_sound_2d(world, snd, ${volume}, ${pitch}, ${startTime})
|
|
142
|
+
ok = True
|
|
143
|
+
except AttributeError:
|
|
144
|
+
try:
|
|
145
|
+
unreal.GameplayStatics.play_sound_2d(world, snd, ${volume}, ${pitch}, ${startTime})
|
|
146
|
+
ok = True
|
|
147
|
+
except AttributeError:
|
|
148
|
+
# Fallback: play at camera location as 2D substitute
|
|
149
|
+
try:
|
|
150
|
+
info = unreal.EditorLevelLibrary.get_level_viewport_camera_info()
|
|
151
|
+
cam_loc = info[0] if isinstance(info, (list, tuple)) and len(info) > 0 else unreal.Vector(0.0, 0.0, 0.0)
|
|
152
|
+
except Exception:
|
|
153
|
+
cam_loc = unreal.Vector(0.0, 0.0, 0.0)
|
|
154
|
+
rot = unreal.Rotator(0.0, 0.0, 0.0)
|
|
155
|
+
unreal.GameplayStatics.spawn_sound_at_location(world, snd, cam_loc, rot, ${volume}, ${pitch}, ${startTime})
|
|
156
|
+
ok = True
|
|
157
|
+
print('RESULT:' + json.dumps({'success': True} if ok else {'success': False, 'error': 'No suitable 2D playback method found'}))
|
|
158
|
+
except Exception as e:
|
|
159
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
160
|
+
`.trim();
|
|
161
|
+
try {
|
|
162
|
+
const resp = await this.bridge.executePython(py);
|
|
163
|
+
const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
|
|
164
|
+
const m = out.match(/RESULT:({.*})/);
|
|
165
|
+
if (m) {
|
|
166
|
+
try {
|
|
167
|
+
const parsed = JSON.parse(m[1]);
|
|
168
|
+
return parsed.success ? { success: true, message: 'Sound2D played' } : { success: false, error: parsed.error };
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
}
|
|
172
|
+
return { success: true, message: 'Sound2D play attempted' };
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
return { success: false, error: `Failed to play sound2D: ${e}` };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Create audio component
|
|
179
|
+
async createAudioComponent(params) {
|
|
180
|
+
const commands = [];
|
|
181
|
+
commands.push(`AddAudioComponent ${params.actorName} ${params.componentName} ${params.soundPath}`);
|
|
182
|
+
if (params.autoPlay !== undefined) {
|
|
183
|
+
commands.push(`SetAudioComponentAutoPlay ${params.actorName}.${params.componentName} ${params.autoPlay}`);
|
|
184
|
+
}
|
|
185
|
+
if (params.is3D !== undefined) {
|
|
186
|
+
commands.push(`SetAudioComponent3D ${params.actorName}.${params.componentName} ${params.is3D}`);
|
|
187
|
+
}
|
|
188
|
+
for (const cmd of commands) {
|
|
189
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
190
|
+
}
|
|
191
|
+
return { success: true, message: `Audio component ${params.componentName} added to ${params.actorName}` };
|
|
192
|
+
}
|
|
193
|
+
// Set sound attenuation
|
|
194
|
+
async setSoundAttenuation(params) {
|
|
195
|
+
const commands = [];
|
|
196
|
+
commands.push(`CreateAttenuationSettings ${params.name}`);
|
|
197
|
+
if (params.innerRadius !== undefined) {
|
|
198
|
+
commands.push(`SetAttenuationInnerRadius ${params.name} ${params.innerRadius}`);
|
|
199
|
+
}
|
|
200
|
+
if (params.falloffDistance !== undefined) {
|
|
201
|
+
commands.push(`SetAttenuationFalloffDistance ${params.name} ${params.falloffDistance}`);
|
|
202
|
+
}
|
|
203
|
+
if (params.attenuationShape) {
|
|
204
|
+
commands.push(`SetAttenuationShape ${params.name} ${params.attenuationShape}`);
|
|
205
|
+
}
|
|
206
|
+
if (params.falloffMode) {
|
|
207
|
+
commands.push(`SetAttenuationFalloffMode ${params.name} ${params.falloffMode}`);
|
|
208
|
+
}
|
|
209
|
+
for (const cmd of commands) {
|
|
210
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
211
|
+
}
|
|
212
|
+
return { success: true, message: `Attenuation settings ${params.name} configured` };
|
|
213
|
+
}
|
|
214
|
+
// Create sound class
|
|
215
|
+
async createSoundClass(params) {
|
|
216
|
+
const commands = [];
|
|
217
|
+
const parent = params.parentClass || 'Master';
|
|
218
|
+
commands.push(`CreateSoundClass ${params.name} ${parent}`);
|
|
219
|
+
if (params.properties) {
|
|
220
|
+
if (params.properties.volume !== undefined) {
|
|
221
|
+
commands.push(`SetSoundClassVolume ${params.name} ${params.properties.volume}`);
|
|
222
|
+
}
|
|
223
|
+
if (params.properties.pitch !== undefined) {
|
|
224
|
+
commands.push(`SetSoundClassPitch ${params.name} ${params.properties.pitch}`);
|
|
225
|
+
}
|
|
226
|
+
if (params.properties.lowPassFilterFrequency !== undefined) {
|
|
227
|
+
commands.push(`SetSoundClassLowPassFilter ${params.name} ${params.properties.lowPassFilterFrequency}`);
|
|
228
|
+
}
|
|
229
|
+
if (params.properties.attenuationDistanceScale !== undefined) {
|
|
230
|
+
commands.push(`SetSoundClassAttenuationScale ${params.name} ${params.properties.attenuationDistanceScale}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const cmd of commands) {
|
|
234
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
235
|
+
}
|
|
236
|
+
return { success: true, message: `Sound class ${params.name} created` };
|
|
237
|
+
}
|
|
238
|
+
// Create sound mix
|
|
239
|
+
async createSoundMix(params) {
|
|
240
|
+
const commands = [];
|
|
241
|
+
commands.push(`CreateSoundMix ${params.name}`);
|
|
242
|
+
if (params.classAdjusters) {
|
|
243
|
+
for (const adjuster of params.classAdjusters) {
|
|
244
|
+
commands.push(`AddSoundMixClassAdjuster ${params.name} ${adjuster.soundClass}`);
|
|
245
|
+
if (adjuster.volumeAdjuster !== undefined) {
|
|
246
|
+
commands.push(`SetSoundMixVolume ${params.name} ${adjuster.soundClass} ${adjuster.volumeAdjuster}`);
|
|
247
|
+
}
|
|
248
|
+
if (adjuster.pitchAdjuster !== undefined) {
|
|
249
|
+
commands.push(`SetSoundMixPitch ${params.name} ${adjuster.soundClass} ${adjuster.pitchAdjuster}`);
|
|
250
|
+
}
|
|
251
|
+
if (adjuster.fadeInTime !== undefined) {
|
|
252
|
+
commands.push(`SetSoundMixFadeIn ${params.name} ${adjuster.soundClass} ${adjuster.fadeInTime}`);
|
|
253
|
+
}
|
|
254
|
+
if (adjuster.fadeOutTime !== undefined) {
|
|
255
|
+
commands.push(`SetSoundMixFadeOut ${params.name} ${adjuster.soundClass} ${adjuster.fadeOutTime}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const cmd of commands) {
|
|
260
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
261
|
+
}
|
|
262
|
+
return { success: true, message: `Sound mix ${params.name} created` };
|
|
263
|
+
}
|
|
264
|
+
// Push/Pop sound mix
|
|
265
|
+
async pushSoundMix(params) {
|
|
266
|
+
const command = `PushSoundMix ${params.mixName}`;
|
|
267
|
+
return this.bridge.executeConsoleCommand(command);
|
|
268
|
+
}
|
|
269
|
+
async popSoundMix(params) {
|
|
270
|
+
const command = `PopSoundMix ${params.mixName}`;
|
|
271
|
+
return this.bridge.executeConsoleCommand(command);
|
|
272
|
+
}
|
|
273
|
+
// Set master volume
|
|
274
|
+
async setMasterVolume(params) {
|
|
275
|
+
// Clamp volume between 0 and 1
|
|
276
|
+
const vol = Math.max(0.0, Math.min(1.0, params.volume));
|
|
277
|
+
// Use the proper Unreal Engine audio command
|
|
278
|
+
// Note: au.Master.Volume is the correct console variable for master volume
|
|
279
|
+
const command = `au.Master.Volume ${vol}`;
|
|
280
|
+
try {
|
|
281
|
+
await this.bridge.executeConsoleCommand(command);
|
|
282
|
+
return { success: true, message: `Master volume set to ${vol}` };
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
// Fallback to Python method if console command fails
|
|
286
|
+
const py = `
|
|
287
|
+
import unreal
|
|
288
|
+
import json
|
|
289
|
+
try:
|
|
290
|
+
# Try using AudioMixerBlueprintLibrary if available
|
|
291
|
+
try:
|
|
292
|
+
unreal.AudioMixerBlueprintLibrary.set_overall_volume_multiplier(${vol})
|
|
293
|
+
print('RESULT:' + json.dumps({'success': True}))
|
|
294
|
+
except AttributeError:
|
|
295
|
+
# Fallback to GameplayStatics method
|
|
296
|
+
try:
|
|
297
|
+
world = unreal.EditorLevelLibrary.get_editor_world()
|
|
298
|
+
unreal.GameplayStatics.set_global_pitch_modulation(world, 1.0, 0.0) # Reset pitch
|
|
299
|
+
unreal.GameplayStatics.set_global_time_dilation(world, 1.0) # Reset time
|
|
300
|
+
# Note: There's no direct master volume in GameplayStatics, use sound class
|
|
301
|
+
print('RESULT:' + json.dumps({'success': False, 'error': 'Master volume control not available, use sound classes instead'}))
|
|
302
|
+
except Exception as e2:
|
|
303
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e2)}))
|
|
304
|
+
except Exception as e:
|
|
305
|
+
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
306
|
+
`.trim();
|
|
307
|
+
try {
|
|
308
|
+
const resp = await this.bridge.executePython(py);
|
|
309
|
+
const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
|
|
310
|
+
const m = out.match(/RESULT:({.*})/);
|
|
311
|
+
if (m) {
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(m[1]);
|
|
314
|
+
return parsed.success
|
|
315
|
+
? { success: true, message: `Master volume set to ${vol}` }
|
|
316
|
+
: { success: false, error: parsed.error };
|
|
317
|
+
}
|
|
318
|
+
catch { }
|
|
319
|
+
}
|
|
320
|
+
return { success: true, message: 'Master volume set command executed' };
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return { success: false, error: `Failed to set master volume: ${e}` };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Create ambient sound
|
|
328
|
+
async createAmbientSound(params) {
|
|
329
|
+
const commands = [];
|
|
330
|
+
commands.push(`SpawnAmbientSound ${params.name} ${params.location.join(' ')} ${params.soundPath}`);
|
|
331
|
+
if (params.volume !== undefined) {
|
|
332
|
+
commands.push(`SetAmbientVolume ${params.name} ${params.volume}`);
|
|
333
|
+
}
|
|
334
|
+
if (params.radius !== undefined) {
|
|
335
|
+
commands.push(`SetAmbientRadius ${params.name} ${params.radius}`);
|
|
336
|
+
}
|
|
337
|
+
if (params.autoPlay !== undefined) {
|
|
338
|
+
commands.push(`SetAmbientAutoPlay ${params.name} ${params.autoPlay}`);
|
|
339
|
+
}
|
|
340
|
+
for (const cmd of commands) {
|
|
341
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
342
|
+
}
|
|
343
|
+
return { success: true, message: `Ambient sound ${params.name} created` };
|
|
344
|
+
}
|
|
345
|
+
// Create reverb zone
|
|
346
|
+
async createReverbZone(params) {
|
|
347
|
+
const commands = [];
|
|
348
|
+
commands.push(`CreateReverbVolume ${params.name} ${params.location.join(' ')} ${params.size.join(' ')}`);
|
|
349
|
+
if (params.reverbEffect) {
|
|
350
|
+
commands.push(`SetReverbEffect ${params.name} ${params.reverbEffect}`);
|
|
351
|
+
}
|
|
352
|
+
if (params.volume !== undefined) {
|
|
353
|
+
commands.push(`SetReverbVolume ${params.name} ${params.volume}`);
|
|
354
|
+
}
|
|
355
|
+
if (params.fadeTime !== undefined) {
|
|
356
|
+
commands.push(`SetReverbFadeTime ${params.name} ${params.fadeTime}`);
|
|
357
|
+
}
|
|
358
|
+
for (const cmd of commands) {
|
|
359
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
360
|
+
}
|
|
361
|
+
return { success: true, message: `Reverb zone ${params.name} created` };
|
|
362
|
+
}
|
|
363
|
+
// Audio analysis
|
|
364
|
+
async enableAudioAnalysis(params) {
|
|
365
|
+
const commands = [];
|
|
366
|
+
commands.push(`EnableAudioAnalysis ${params.enabled}`);
|
|
367
|
+
if (params.enabled && params.fftSize) {
|
|
368
|
+
commands.push(`SetFFTSize ${params.fftSize}`);
|
|
369
|
+
}
|
|
370
|
+
if (params.enabled && params.outputType) {
|
|
371
|
+
commands.push(`SetAudioAnalysisOutput ${params.outputType}`);
|
|
372
|
+
}
|
|
373
|
+
for (const cmd of commands) {
|
|
374
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
375
|
+
}
|
|
376
|
+
return { success: true, message: `Audio analysis ${params.enabled ? 'enabled' : 'disabled'}` };
|
|
377
|
+
}
|
|
378
|
+
// Stop all sounds
|
|
379
|
+
async stopAllSounds() {
|
|
380
|
+
return this.bridge.executeConsoleCommand('StopAllSounds');
|
|
381
|
+
}
|
|
382
|
+
// Fade sound
|
|
383
|
+
async fadeSound(params) {
|
|
384
|
+
const type = params.fadeType || 'FadeTo';
|
|
385
|
+
const command = `${type}Sound ${params.soundName} ${params.targetVolume} ${params.fadeTime}`;
|
|
386
|
+
return this.bridge.executeConsoleCommand(command);
|
|
387
|
+
}
|
|
388
|
+
// Set doppler effect
|
|
389
|
+
async setDopplerEffect(params) {
|
|
390
|
+
const commands = [];
|
|
391
|
+
commands.push(`EnableDoppler ${params.enabled}`);
|
|
392
|
+
if (params.scale !== undefined) {
|
|
393
|
+
commands.push(`SetDopplerScale ${params.scale}`);
|
|
394
|
+
}
|
|
395
|
+
for (const cmd of commands) {
|
|
396
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
397
|
+
}
|
|
398
|
+
return { success: true, message: `Doppler effect ${params.enabled ? 'enabled' : 'disabled'}` };
|
|
399
|
+
}
|
|
400
|
+
// Audio occlusion
|
|
401
|
+
async setAudioOcclusion(params) {
|
|
402
|
+
const commands = [];
|
|
403
|
+
commands.push(`EnableAudioOcclusion ${params.enabled}`);
|
|
404
|
+
if (params.lowPassFilterFrequency !== undefined) {
|
|
405
|
+
commands.push(`SetOcclusionLowPassFilter ${params.lowPassFilterFrequency}`);
|
|
406
|
+
}
|
|
407
|
+
if (params.volumeAttenuation !== undefined) {
|
|
408
|
+
commands.push(`SetOcclusionVolumeAttenuation ${params.volumeAttenuation}`);
|
|
409
|
+
}
|
|
410
|
+
for (const cmd of commands) {
|
|
411
|
+
await this.bridge.executeConsoleCommand(cmd);
|
|
412
|
+
}
|
|
413
|
+
return { success: true, message: `Audio occlusion ${params.enabled ? 'enabled' : 'disabled'}` };
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
|
+
export declare class BlueprintTools {
|
|
3
|
+
private bridge;
|
|
4
|
+
constructor(bridge: UnrealBridge);
|
|
5
|
+
/**
|
|
6
|
+
* Create Blueprint
|
|
7
|
+
*/
|
|
8
|
+
createBlueprint(params: {
|
|
9
|
+
name: string;
|
|
10
|
+
blueprintType: 'Actor' | 'Pawn' | 'Character' | 'GameMode' | 'PlayerController' | 'HUD' | 'ActorComponent';
|
|
11
|
+
savePath?: string;
|
|
12
|
+
parentClass?: string;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
message: string;
|
|
16
|
+
error: string | undefined;
|
|
17
|
+
path?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
success: boolean;
|
|
20
|
+
message: string;
|
|
21
|
+
path: string;
|
|
22
|
+
error?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
success: boolean;
|
|
25
|
+
error: string;
|
|
26
|
+
message?: undefined;
|
|
27
|
+
path?: undefined;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Add Component to Blueprint
|
|
31
|
+
*/
|
|
32
|
+
addComponent(params: {
|
|
33
|
+
blueprintName: string;
|
|
34
|
+
componentType: string;
|
|
35
|
+
componentName: string;
|
|
36
|
+
attachTo?: string;
|
|
37
|
+
transform?: {
|
|
38
|
+
location?: [number, number, number];
|
|
39
|
+
rotation?: [number, number, number];
|
|
40
|
+
scale?: [number, number, number];
|
|
41
|
+
};
|
|
42
|
+
}): Promise<{
|
|
43
|
+
success: boolean;
|
|
44
|
+
message: string;
|
|
45
|
+
error?: undefined;
|
|
46
|
+
} | {
|
|
47
|
+
success: boolean;
|
|
48
|
+
message: string;
|
|
49
|
+
error: string;
|
|
50
|
+
} | {
|
|
51
|
+
success: boolean;
|
|
52
|
+
error: string;
|
|
53
|
+
message?: undefined;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Add Variable to Blueprint
|
|
57
|
+
*/
|
|
58
|
+
addVariable(params: {
|
|
59
|
+
blueprintName: string;
|
|
60
|
+
variableName: string;
|
|
61
|
+
variableType: string;
|
|
62
|
+
defaultValue?: any;
|
|
63
|
+
category?: string;
|
|
64
|
+
isReplicated?: boolean;
|
|
65
|
+
isPublic?: boolean;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
success: boolean;
|
|
68
|
+
message: string;
|
|
69
|
+
error?: undefined;
|
|
70
|
+
} | {
|
|
71
|
+
success: boolean;
|
|
72
|
+
error: string;
|
|
73
|
+
message?: undefined;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* Add Function to Blueprint
|
|
77
|
+
*/
|
|
78
|
+
addFunction(params: {
|
|
79
|
+
blueprintName: string;
|
|
80
|
+
functionName: string;
|
|
81
|
+
inputs?: Array<{
|
|
82
|
+
name: string;
|
|
83
|
+
type: string;
|
|
84
|
+
}>;
|
|
85
|
+
outputs?: Array<{
|
|
86
|
+
name: string;
|
|
87
|
+
type: string;
|
|
88
|
+
}>;
|
|
89
|
+
isPublic?: boolean;
|
|
90
|
+
category?: string;
|
|
91
|
+
}): Promise<{
|
|
92
|
+
success: boolean;
|
|
93
|
+
message: string;
|
|
94
|
+
error?: undefined;
|
|
95
|
+
} | {
|
|
96
|
+
success: boolean;
|
|
97
|
+
error: string;
|
|
98
|
+
message?: undefined;
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Add Event to Blueprint
|
|
102
|
+
*/
|
|
103
|
+
addEvent(params: {
|
|
104
|
+
blueprintName: string;
|
|
105
|
+
eventType: 'BeginPlay' | 'Tick' | 'EndPlay' | 'BeginOverlap' | 'EndOverlap' | 'Hit' | 'Custom';
|
|
106
|
+
customEventName?: string;
|
|
107
|
+
parameters?: Array<{
|
|
108
|
+
name: string;
|
|
109
|
+
type: string;
|
|
110
|
+
}>;
|
|
111
|
+
}): Promise<{
|
|
112
|
+
success: boolean;
|
|
113
|
+
message: string;
|
|
114
|
+
error?: undefined;
|
|
115
|
+
} | {
|
|
116
|
+
success: boolean;
|
|
117
|
+
error: string;
|
|
118
|
+
message?: undefined;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Compile Blueprint
|
|
122
|
+
*/
|
|
123
|
+
compileBlueprint(params: {
|
|
124
|
+
blueprintName: string;
|
|
125
|
+
saveAfterCompile?: boolean;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
success: boolean;
|
|
128
|
+
message: string;
|
|
129
|
+
error?: undefined;
|
|
130
|
+
} | {
|
|
131
|
+
success: boolean;
|
|
132
|
+
error: string;
|
|
133
|
+
message?: undefined;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Get default parent class for blueprint type
|
|
137
|
+
*/
|
|
138
|
+
private _getDefaultParentClass;
|
|
139
|
+
/**
|
|
140
|
+
* Helper function to execute console commands
|
|
141
|
+
*/
|
|
142
|
+
private _executeCommand;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=blueprint.d.ts.map
|