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.
Files changed (155) hide show
  1. package/.dockerignore +57 -0
  2. package/.env.production +25 -0
  3. package/.eslintrc.json +54 -0
  4. package/.github/workflows/publish-mcp.yml +75 -0
  5. package/Dockerfile +54 -0
  6. package/LICENSE +21 -0
  7. package/Public/icon.png +0 -0
  8. package/README.md +209 -0
  9. package/claude_desktop_config_example.json +13 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.js +7 -0
  12. package/dist/index.d.ts +31 -0
  13. package/dist/index.js +484 -0
  14. package/dist/prompts/index.d.ts +14 -0
  15. package/dist/prompts/index.js +38 -0
  16. package/dist/python-utils.d.ts +29 -0
  17. package/dist/python-utils.js +54 -0
  18. package/dist/resources/actors.d.ts +13 -0
  19. package/dist/resources/actors.js +83 -0
  20. package/dist/resources/assets.d.ts +23 -0
  21. package/dist/resources/assets.js +245 -0
  22. package/dist/resources/levels.d.ts +17 -0
  23. package/dist/resources/levels.js +94 -0
  24. package/dist/tools/actors.d.ts +51 -0
  25. package/dist/tools/actors.js +459 -0
  26. package/dist/tools/animation.d.ts +196 -0
  27. package/dist/tools/animation.js +579 -0
  28. package/dist/tools/assets.d.ts +21 -0
  29. package/dist/tools/assets.js +304 -0
  30. package/dist/tools/audio.d.ts +170 -0
  31. package/dist/tools/audio.js +416 -0
  32. package/dist/tools/blueprint.d.ts +144 -0
  33. package/dist/tools/blueprint.js +652 -0
  34. package/dist/tools/build_environment_advanced.d.ts +66 -0
  35. package/dist/tools/build_environment_advanced.js +484 -0
  36. package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
  37. package/dist/tools/consolidated-tool-definitions.js +607 -0
  38. package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
  39. package/dist/tools/consolidated-tool-handlers.js +1050 -0
  40. package/dist/tools/debug.d.ts +185 -0
  41. package/dist/tools/debug.js +265 -0
  42. package/dist/tools/editor.d.ts +88 -0
  43. package/dist/tools/editor.js +365 -0
  44. package/dist/tools/engine.d.ts +30 -0
  45. package/dist/tools/engine.js +36 -0
  46. package/dist/tools/foliage.d.ts +155 -0
  47. package/dist/tools/foliage.js +525 -0
  48. package/dist/tools/introspection.d.ts +98 -0
  49. package/dist/tools/introspection.js +683 -0
  50. package/dist/tools/landscape.d.ts +158 -0
  51. package/dist/tools/landscape.js +375 -0
  52. package/dist/tools/level.d.ts +110 -0
  53. package/dist/tools/level.js +362 -0
  54. package/dist/tools/lighting.d.ts +159 -0
  55. package/dist/tools/lighting.js +1179 -0
  56. package/dist/tools/materials.d.ts +34 -0
  57. package/dist/tools/materials.js +146 -0
  58. package/dist/tools/niagara.d.ts +145 -0
  59. package/dist/tools/niagara.js +289 -0
  60. package/dist/tools/performance.d.ts +163 -0
  61. package/dist/tools/performance.js +412 -0
  62. package/dist/tools/physics.d.ts +189 -0
  63. package/dist/tools/physics.js +784 -0
  64. package/dist/tools/rc.d.ts +110 -0
  65. package/dist/tools/rc.js +363 -0
  66. package/dist/tools/sequence.d.ts +112 -0
  67. package/dist/tools/sequence.js +675 -0
  68. package/dist/tools/tool-definitions.d.ts +4919 -0
  69. package/dist/tools/tool-definitions.js +891 -0
  70. package/dist/tools/tool-handlers.d.ts +47 -0
  71. package/dist/tools/tool-handlers.js +830 -0
  72. package/dist/tools/ui.d.ts +171 -0
  73. package/dist/tools/ui.js +337 -0
  74. package/dist/tools/visual.d.ts +29 -0
  75. package/dist/tools/visual.js +67 -0
  76. package/dist/types/env.d.ts +10 -0
  77. package/dist/types/env.js +18 -0
  78. package/dist/types/index.d.ts +323 -0
  79. package/dist/types/index.js +28 -0
  80. package/dist/types/tool-types.d.ts +274 -0
  81. package/dist/types/tool-types.js +13 -0
  82. package/dist/unreal-bridge.d.ts +126 -0
  83. package/dist/unreal-bridge.js +992 -0
  84. package/dist/utils/cache-manager.d.ts +64 -0
  85. package/dist/utils/cache-manager.js +176 -0
  86. package/dist/utils/error-handler.d.ts +66 -0
  87. package/dist/utils/error-handler.js +243 -0
  88. package/dist/utils/errors.d.ts +133 -0
  89. package/dist/utils/errors.js +256 -0
  90. package/dist/utils/http.d.ts +26 -0
  91. package/dist/utils/http.js +135 -0
  92. package/dist/utils/logger.d.ts +12 -0
  93. package/dist/utils/logger.js +32 -0
  94. package/dist/utils/normalize.d.ts +17 -0
  95. package/dist/utils/normalize.js +49 -0
  96. package/dist/utils/response-validator.d.ts +34 -0
  97. package/dist/utils/response-validator.js +121 -0
  98. package/dist/utils/safe-json.d.ts +4 -0
  99. package/dist/utils/safe-json.js +97 -0
  100. package/dist/utils/stdio-redirect.d.ts +2 -0
  101. package/dist/utils/stdio-redirect.js +20 -0
  102. package/dist/utils/validation.d.ts +50 -0
  103. package/dist/utils/validation.js +173 -0
  104. package/mcp-config-example.json +14 -0
  105. package/package.json +63 -0
  106. package/server.json +60 -0
  107. package/src/cli.ts +7 -0
  108. package/src/index.ts +543 -0
  109. package/src/prompts/index.ts +51 -0
  110. package/src/python/editor_compat.py +181 -0
  111. package/src/python-utils.ts +57 -0
  112. package/src/resources/actors.ts +92 -0
  113. package/src/resources/assets.ts +251 -0
  114. package/src/resources/levels.ts +83 -0
  115. package/src/tools/actors.ts +480 -0
  116. package/src/tools/animation.ts +713 -0
  117. package/src/tools/assets.ts +305 -0
  118. package/src/tools/audio.ts +548 -0
  119. package/src/tools/blueprint.ts +736 -0
  120. package/src/tools/build_environment_advanced.ts +526 -0
  121. package/src/tools/consolidated-tool-definitions.ts +619 -0
  122. package/src/tools/consolidated-tool-handlers.ts +1093 -0
  123. package/src/tools/debug.ts +368 -0
  124. package/src/tools/editor.ts +360 -0
  125. package/src/tools/engine.ts +32 -0
  126. package/src/tools/foliage.ts +652 -0
  127. package/src/tools/introspection.ts +778 -0
  128. package/src/tools/landscape.ts +523 -0
  129. package/src/tools/level.ts +410 -0
  130. package/src/tools/lighting.ts +1316 -0
  131. package/src/tools/materials.ts +148 -0
  132. package/src/tools/niagara.ts +312 -0
  133. package/src/tools/performance.ts +549 -0
  134. package/src/tools/physics.ts +924 -0
  135. package/src/tools/rc.ts +437 -0
  136. package/src/tools/sequence.ts +791 -0
  137. package/src/tools/tool-definitions.ts +907 -0
  138. package/src/tools/tool-handlers.ts +941 -0
  139. package/src/tools/ui.ts +499 -0
  140. package/src/tools/visual.ts +60 -0
  141. package/src/types/env.ts +27 -0
  142. package/src/types/index.ts +414 -0
  143. package/src/types/tool-types.ts +343 -0
  144. package/src/unreal-bridge.ts +1118 -0
  145. package/src/utils/cache-manager.ts +213 -0
  146. package/src/utils/error-handler.ts +320 -0
  147. package/src/utils/errors.ts +312 -0
  148. package/src/utils/http.ts +184 -0
  149. package/src/utils/logger.ts +30 -0
  150. package/src/utils/normalize.ts +54 -0
  151. package/src/utils/response-validator.ts +145 -0
  152. package/src/utils/safe-json.ts +112 -0
  153. package/src/utils/stdio-redirect.ts +18 -0
  154. package/src/utils/validation.ts +212 -0
  155. package/tsconfig.json +33 -0
@@ -0,0 +1,362 @@
1
+ export class LevelTools {
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
+ // Load level (using LevelEditorSubsystem to avoid crashes)
20
+ async loadLevel(params) {
21
+ if (params.streaming) {
22
+ // Try to add as streaming level
23
+ const py = `\nimport unreal\ntry:\n # Use UnrealEditorSubsystem to get editor world\n ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)\n world = ues.get_editor_world() if ues else None\n if world:\n unreal.EditorLevelUtils.add_level_to_world(world, r"${params.levelPath}", unreal.LevelStreamingKismet)\n print('RESULT:{\\'success\\': True}')\n else:\n print('RESULT:{\\'success\\': False, \\'error\\': \\'No editor world\\'}')\nexcept Exception as e:\n print('RESULT:{\\'success\\': False, \\'error\\': \\'%s\\'}' % str(e))\n`.trim();
24
+ try {
25
+ const resp = await this.bridge.executePython(py);
26
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
27
+ const m = out.match(/RESULT:({.*})/);
28
+ if (m) {
29
+ try {
30
+ const parsed = JSON.parse(m[1].replace(/'/g, '"'));
31
+ if (parsed.success)
32
+ return { success: true, message: 'Streaming level added' };
33
+ }
34
+ catch { }
35
+ }
36
+ }
37
+ catch { }
38
+ // Fallback to console
39
+ return this.bridge.executeConsoleCommand(`LoadStreamLevel ${params.levelPath}`);
40
+ }
41
+ else {
42
+ // Use LevelEditorSubsystem.load_level() to avoid crashes
43
+ // This properly handles the WorldContext and avoids the assertion failure
44
+ const python = `
45
+ import unreal
46
+ try:
47
+ # Use LevelEditorSubsystem which properly handles WorldContext
48
+ les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
49
+ if les:
50
+ # load_level closes the current level and opens a new one
51
+ # This is the proper way to switch levels in the editor
52
+ success = les.load_level(r"${params.levelPath}")
53
+ if success:
54
+ print('RESULT:{"success": true, "message": "Level loaded successfully"}')
55
+ else:
56
+ print('RESULT:{"success": false, "error": "Failed to load level"}')
57
+ else:
58
+ print('RESULT:{"success": false, "error": "LevelEditorSubsystem not available"}')
59
+ except Exception as e:
60
+ print('RESULT:{"success": false, "error": "' + str(e).replace('"','\\"'
61
+ `.trim();
62
+ try {
63
+ const resp = await this.bridge.executePython(python);
64
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
65
+ const m = out.match(/RESULT:({.*})/);
66
+ if (m) {
67
+ try {
68
+ const parsed = JSON.parse(m[1]);
69
+ return parsed.success
70
+ ? { success: true, message: parsed.message || `Level ${params.levelPath} loaded` }
71
+ : { success: false, error: parsed.error || 'Failed to load level' };
72
+ }
73
+ catch { }
74
+ }
75
+ // If we get here but no error was thrown, assume success
76
+ return { success: true, message: `Level ${params.levelPath} loaded` };
77
+ }
78
+ catch (e) {
79
+ return { success: false, error: `Failed to load level: ${e}` };
80
+ }
81
+ }
82
+ }
83
+ // Save current level
84
+ async saveLevel(_params) {
85
+ // Use Python EditorLevelLibrary.save_current_level for reliability
86
+ const python = `
87
+ import unreal
88
+ try:
89
+ # Attempt to reduce source control prompts (best-effort, may be a no-op depending on UE version)
90
+ try:
91
+ prefs = unreal.SourceControlPreferences()
92
+ try:
93
+ prefs.set_enable_source_control(False)
94
+ except Exception:
95
+ try:
96
+ prefs.enable_source_control = False
97
+ except Exception:
98
+ pass
99
+ except Exception:
100
+ pass
101
+
102
+ # Determine if level is dirty and save via LevelEditorSubsystem when possible
103
+ try:
104
+ world = None
105
+ try:
106
+ world = unreal.EditorSubsystemLibrary.get_editor_world()
107
+ except Exception:
108
+ try:
109
+ world = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem).get_editor_world()
110
+ except Exception:
111
+ world = None
112
+ pkg_path = None
113
+ try:
114
+ if world is not None:
115
+ full = world.get_path_name()
116
+ pkg_path = full.split('.')[0] if '.' in full else full
117
+ except Exception:
118
+ pkg_path = None
119
+ if pkg_path and not unreal.EditorAssetLibrary.is_asset_dirty(pkg_path):
120
+ print('RESULT:{"success": true, "skipped": true, "reason": "Level not dirty"}')
121
+ raise SystemExit(0)
122
+ except Exception:
123
+ pass
124
+
125
+ # Save using LevelEditorSubsystem to avoid deprecation
126
+ saved = False
127
+ try:
128
+ les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
129
+ if les:
130
+ les.save_current_level()
131
+ saved = True
132
+ except Exception:
133
+ pass
134
+ if not saved:
135
+ # No fallback available - LevelEditorSubsystem is required
136
+ raise Exception('LevelEditorSubsystem not available')
137
+ print('RESULT:{"success": true}')
138
+ except Exception as e:
139
+ print('RESULT:{"success": false, "error": "' + str(e).replace('"','\\"') + '"}')
140
+ `.trim();
141
+ try {
142
+ const resp = await this.bridge.executePython(python);
143
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
144
+ const m = out.match(/RESULT:({.*})/);
145
+ if (m) {
146
+ try {
147
+ const parsed = JSON.parse(m[1]);
148
+ return parsed.success ? { success: true, message: 'Level saved' } : { success: false, error: parsed.error };
149
+ }
150
+ catch { }
151
+ }
152
+ return { success: true, message: 'Level saved' };
153
+ }
154
+ catch (e) {
155
+ return { success: false, error: `Failed to save level: ${e}` };
156
+ }
157
+ }
158
+ // Create new level (Python via LevelEditorSubsystem)
159
+ async createLevel(params) {
160
+ const basePath = params.savePath || '/Game/Maps';
161
+ const isPartitioned = true; // default to World Partition for UE5
162
+ const fullPath = `${basePath}/${params.levelName}`;
163
+ const py = `
164
+ import unreal
165
+ try:
166
+ les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
167
+ if les:
168
+ les.new_level(r"${fullPath}", ${isPartitioned ? 'True' : 'False'})
169
+ print('RESULT:{"success": True, "message": "Level created"}')
170
+ else:
171
+ print('RESULT:{"success": False, "error": "LevelEditorSubsystem not available"}')
172
+ except Exception as e:
173
+ print('RESULT:{"success": False, "error": "' + str(e) + '"}')
174
+ `.trim();
175
+ try {
176
+ const resp = await this.bridge.executePython(py);
177
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
178
+ const m = out.match(/RESULT:({.*})/);
179
+ if (m) {
180
+ try {
181
+ const parsed = JSON.parse(m[1].replace(/'/g, '"'));
182
+ return parsed.success ? { success: true, message: parsed.message } : { success: false, error: parsed.error };
183
+ }
184
+ catch { }
185
+ }
186
+ return { success: true, message: 'Level creation attempted' };
187
+ }
188
+ catch (e) {
189
+ return { success: false, error: `Failed to create level: ${e}` };
190
+ }
191
+ }
192
+ // Stream level (Python attempt with fallback)
193
+ async streamLevel(params) {
194
+ const py = `\nimport unreal\ntry:\n # Use UnrealEditorSubsystem to get editor world\n ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)\n world = ues.get_editor_world() if ues else None\n if world:\n # Find streaming level by name and set flags\n updated = False\n for sl in world.get_streaming_levels():\n try:\n name = sl.get_world_asset_package_name() if hasattr(sl, 'get_world_asset_package_name') else str(sl.get_editor_property('world_asset'))\n if name and name.endswith('/${params.levelName}'):\n try: sl.set_should_be_loaded(${params.shouldBeLoaded ? 'True' : 'False'})\n except Exception: pass\n try: sl.set_should_be_visible(${params.shouldBeVisible ? 'True' : 'False'})\n except Exception: pass\n updated = True\n break\n except Exception: pass\n print('RESULT:{\\'success\\': %s}' % ('True' if updated else 'False'))\n else:\n print('RESULT:{\\'success\\': False, \\'error\\': \\'No editor world\\'}')\nexcept Exception as e:\n print('RESULT:{\\'success\\': False, \\'error\\': \\'%s\\'}' % str(e))\n`.trim();
195
+ try {
196
+ const resp = await this.bridge.executePython(py);
197
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
198
+ const m = out.match(/RESULT:({.*})/);
199
+ if (m) {
200
+ try {
201
+ const parsed = JSON.parse(m[1].replace(/'/g, '"'));
202
+ if (parsed.success)
203
+ return { success: true, message: 'Streaming level updated' };
204
+ }
205
+ catch { }
206
+ }
207
+ }
208
+ catch { }
209
+ // Fallback
210
+ const loadCmd = params.shouldBeLoaded ? 'Load' : 'Unload';
211
+ const visCmd = params.shouldBeVisible ? 'Show' : 'Hide';
212
+ const command = `StreamLevel ${params.levelName} ${loadCmd} ${visCmd}`;
213
+ return this.bridge.executeConsoleCommand(command);
214
+ }
215
+ // World composition
216
+ async setupWorldComposition(params) {
217
+ const commands = [];
218
+ if (params.enableComposition) {
219
+ commands.push('EnableWorldComposition');
220
+ if (params.tileSize) {
221
+ commands.push(`SetWorldTileSize ${params.tileSize}`);
222
+ }
223
+ if (params.distanceStreaming) {
224
+ commands.push(`EnableDistanceStreaming ${params.streamingDistance || 5000}`);
225
+ }
226
+ }
227
+ else {
228
+ commands.push('DisableWorldComposition');
229
+ }
230
+ for (const cmd of commands) {
231
+ await this.bridge.executeConsoleCommand(cmd);
232
+ }
233
+ return { success: true, message: 'World composition configured' };
234
+ }
235
+ // Level blueprint
236
+ async editLevelBlueprint(params) {
237
+ const command = `OpenLevelBlueprint ${params.eventType}`;
238
+ return this.bridge.executeConsoleCommand(command);
239
+ }
240
+ // Sub-levels
241
+ async createSubLevel(params) {
242
+ const command = `CreateSubLevel ${params.name} ${params.type} ${params.parent || 'None'}`;
243
+ return this.bridge.executeConsoleCommand(command);
244
+ }
245
+ // World settings
246
+ async setWorldSettings(params) {
247
+ const commands = [];
248
+ if (params.gravity !== undefined) {
249
+ commands.push(`SetWorldGravity ${params.gravity}`);
250
+ }
251
+ if (params.worldScale !== undefined) {
252
+ commands.push(`SetWorldToMeters ${params.worldScale}`);
253
+ }
254
+ if (params.gameMode) {
255
+ commands.push(`SetGameMode ${params.gameMode}`);
256
+ }
257
+ if (params.defaultPawn) {
258
+ commands.push(`SetDefaultPawn ${params.defaultPawn}`);
259
+ }
260
+ if (params.killZ !== undefined) {
261
+ commands.push(`SetKillZ ${params.killZ}`);
262
+ }
263
+ for (const cmd of commands) {
264
+ await this.bridge.executeConsoleCommand(cmd);
265
+ }
266
+ return { success: true, message: 'World settings updated' };
267
+ }
268
+ // Level bounds
269
+ async setLevelBounds(params) {
270
+ const command = `SetLevelBounds ${params.min.join(',')} ${params.max.join(',')}`;
271
+ return this.bridge.executeConsoleCommand(command);
272
+ }
273
+ // Navigation mesh
274
+ async buildNavMesh(params) {
275
+ // Use Python API for safer navigation mesh building to avoid crashes
276
+ const py = `
277
+ import unreal
278
+ import json
279
+ try:
280
+ # Check if navigation system exists first
281
+ nav_system = unreal.EditorSubsystemLibrary.get_editor_subsystem(unreal.NavigationSystemV1)
282
+ if not nav_system:
283
+ # Try alternative method
284
+ # Try to get world via UnrealEditorSubsystem
285
+ ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
286
+ world = ues.get_editor_world() if ues else None
287
+ nav_system = unreal.NavigationSystemV1.get_navigation_system(world) if world else None
288
+
289
+ if nav_system:
290
+ # Use the safe Python API method instead of console commands
291
+ if ${params.rebuildAll ? 'True' : 'False'}:
292
+ # Rebuild all navigation
293
+ nav_system.navigation_build_async()
294
+ print('RESULT:' + json.dumps({'success': True, 'message': 'Navigation rebuild started'}))
295
+ else:
296
+ # Update navigation for selected actors only
297
+ # Use EditorActorSubsystem to get selected actors
298
+ actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
299
+ selected_actors = actor_subsystem.get_selected_level_actors() if actor_subsystem else []
300
+ if selected_actors:
301
+ for actor in selected_actors:
302
+ nav_system.update_nav_octree(actor)
303
+ print('RESULT:' + json.dumps({'success': True, 'message': f'Navigation updated for {len(selected_actors)} actors'}))
304
+ else:
305
+ # If nothing selected, do a safe incremental update
306
+ nav_system.update(0.0)
307
+ print('RESULT:' + json.dumps({'success': True, 'message': 'Navigation incremental update performed'}))
308
+ else:
309
+ # Navigation system not available - likely no nav mesh in level
310
+ print('RESULT:' + json.dumps({'success': False, 'error': 'Navigation system not available. Add a NavMeshBoundsVolume to the level first.'}))
311
+ except AttributeError as e:
312
+ # Some methods might not be available in all UE versions
313
+ print('RESULT:' + json.dumps({'success': False, 'error': f'Navigation API not available: {str(e)}'}))
314
+ except Exception as e:
315
+ print('RESULT:' + json.dumps({'success': False, 'error': f'Navigation build failed: {str(e)}'}))
316
+ `.trim();
317
+ try {
318
+ const resp = await this.bridge.executePython(py);
319
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
320
+ const m = out.match(/RESULT:({.*})/);
321
+ if (m) {
322
+ try {
323
+ const parsed = JSON.parse(m[1]);
324
+ return parsed.success
325
+ ? { success: true, message: parsed.message }
326
+ : { success: false, error: parsed.error };
327
+ }
328
+ catch { }
329
+ }
330
+ // Fallback message if no clear result
331
+ return { success: true, message: 'Navigation mesh build attempted' };
332
+ }
333
+ catch (e) {
334
+ // If Python fails, return error instead of trying console command that crashes
335
+ return {
336
+ success: false,
337
+ error: `Navigation build not available: ${e}. Please ensure a NavMeshBoundsVolume exists in the level.`
338
+ };
339
+ }
340
+ }
341
+ // Level visibility
342
+ async setLevelVisibility(params) {
343
+ const command = `SetLevelVisibility ${params.levelName} ${params.visible}`;
344
+ return this.bridge.executeConsoleCommand(command);
345
+ }
346
+ // World origin
347
+ async setWorldOrigin(params) {
348
+ const command = `SetWorldOriginLocation ${params.location.join(' ')}`;
349
+ return this.bridge.executeConsoleCommand(command);
350
+ }
351
+ // Level streaming volumes
352
+ async createStreamingVolume(params) {
353
+ const command = `CreateStreamingVolume ${params.levelName} ${params.position.join(' ')} ${params.size.join(' ')} ${params.streamingDistance || 0}`;
354
+ return this.bridge.executeConsoleCommand(command);
355
+ }
356
+ // Level LOD
357
+ async setLevelLOD(params) {
358
+ const command = `SetLevelLOD ${params.levelName} ${params.lodLevel} ${params.distance}`;
359
+ return this.bridge.executeConsoleCommand(command);
360
+ }
361
+ }
362
+ //# sourceMappingURL=level.js.map
@@ -0,0 +1,159 @@
1
+ import { UnrealBridge } from '../unreal-bridge.js';
2
+ export declare class LightingTools {
3
+ private bridge;
4
+ constructor(bridge: UnrealBridge);
5
+ private escapePythonString;
6
+ private ensurePythonSpawnSucceeded;
7
+ private _executeCommand;
8
+ createDirectionalLight(params: {
9
+ name: string;
10
+ intensity?: number;
11
+ color?: [number, number, number];
12
+ rotation?: [number, number, number];
13
+ castShadows?: boolean;
14
+ temperature?: number;
15
+ }): Promise<{
16
+ success: boolean;
17
+ message: string;
18
+ }>;
19
+ createPointLight(params: {
20
+ name: string;
21
+ location?: [number, number, number];
22
+ intensity?: number;
23
+ radius?: number;
24
+ color?: [number, number, number];
25
+ falloffExponent?: number;
26
+ castShadows?: boolean;
27
+ }): Promise<{
28
+ success: boolean;
29
+ message: string;
30
+ }>;
31
+ createSpotLight(params: {
32
+ name: string;
33
+ location: [number, number, number];
34
+ rotation: [number, number, number];
35
+ intensity?: number;
36
+ innerCone?: number;
37
+ outerCone?: number;
38
+ radius?: number;
39
+ color?: [number, number, number];
40
+ castShadows?: boolean;
41
+ }): Promise<{
42
+ success: boolean;
43
+ message: string;
44
+ }>;
45
+ createRectLight(params: {
46
+ name: string;
47
+ location: [number, number, number];
48
+ rotation: [number, number, number];
49
+ width?: number;
50
+ height?: number;
51
+ intensity?: number;
52
+ color?: [number, number, number];
53
+ }): Promise<{
54
+ success: boolean;
55
+ message: string;
56
+ }>;
57
+ createSkyLight(params: {
58
+ name: string;
59
+ sourceType?: 'CapturedScene' | 'SpecifiedCubemap';
60
+ cubemapPath?: string;
61
+ intensity?: number;
62
+ recapture?: boolean;
63
+ }): Promise<{
64
+ success: boolean;
65
+ message: string;
66
+ error?: undefined;
67
+ } | {
68
+ success: boolean;
69
+ error: any;
70
+ message?: undefined;
71
+ }>;
72
+ ensureSingleSkyLight(params?: {
73
+ name?: string;
74
+ recapture?: boolean;
75
+ }): Promise<{
76
+ success: boolean;
77
+ removed: any;
78
+ message: string;
79
+ } | {
80
+ success: boolean;
81
+ message: string;
82
+ removed?: undefined;
83
+ }>;
84
+ setupGlobalIllumination(params: {
85
+ method: 'Lightmass' | 'LumenGI' | 'ScreenSpace' | 'None';
86
+ quality?: 'Low' | 'Medium' | 'High' | 'Epic';
87
+ indirectLightingIntensity?: number;
88
+ bounces?: number;
89
+ }): Promise<{
90
+ success: boolean;
91
+ message: string;
92
+ }>;
93
+ configureShadows(params: {
94
+ shadowQuality?: 'Low' | 'Medium' | 'High' | 'Epic';
95
+ cascadedShadows?: boolean;
96
+ shadowDistance?: number;
97
+ contactShadows?: boolean;
98
+ rayTracedShadows?: boolean;
99
+ }): Promise<{
100
+ success: boolean;
101
+ message: string;
102
+ }>;
103
+ buildLighting(params: {
104
+ quality?: 'Preview' | 'Medium' | 'High' | 'Production';
105
+ buildOnlySelected?: boolean;
106
+ buildReflectionCaptures?: boolean;
107
+ }): Promise<any>;
108
+ createLightingEnabledLevel(params?: {
109
+ levelName?: string;
110
+ copyActors?: boolean;
111
+ useTemplate?: boolean;
112
+ }): Promise<any>;
113
+ createLightmassVolume(params: {
114
+ name: string;
115
+ location: [number, number, number];
116
+ size: [number, number, number];
117
+ }): Promise<{
118
+ success: boolean;
119
+ message: string;
120
+ error?: undefined;
121
+ } | {
122
+ success: boolean;
123
+ error: any;
124
+ message?: undefined;
125
+ }>;
126
+ setExposure(params: {
127
+ method: 'Manual' | 'Auto';
128
+ compensationValue?: number;
129
+ minBrightness?: number;
130
+ maxBrightness?: number;
131
+ }): Promise<{
132
+ success: boolean;
133
+ message: string;
134
+ }>;
135
+ setAmbientOcclusion(params: {
136
+ enabled: boolean;
137
+ intensity?: number;
138
+ radius?: number;
139
+ quality?: 'Low' | 'Medium' | 'High';
140
+ }): Promise<{
141
+ success: boolean;
142
+ message: string;
143
+ }>;
144
+ setupVolumetricFog(params: {
145
+ enabled: boolean;
146
+ density?: number;
147
+ scatteringIntensity?: number;
148
+ fogHeight?: number;
149
+ }): Promise<{
150
+ success: boolean;
151
+ message: string;
152
+ error?: undefined;
153
+ } | {
154
+ success: boolean;
155
+ error: any;
156
+ message?: undefined;
157
+ }>;
158
+ }
159
+ //# sourceMappingURL=lighting.d.ts.map