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,523 @@
1
+ // Landscape tools for Unreal Engine with UE 5.6 World Partition support
2
+ import { UnrealBridge } from '../unreal-bridge.js';
3
+
4
+ export class LandscapeTools {
5
+ constructor(private bridge: UnrealBridge) {}
6
+
7
+ // Execute console command
8
+ private async _executeCommand(command: string) {
9
+ return this.bridge.httpCall('/remote/object/call', 'PUT', {
10
+ objectPath: '/Script/Engine.Default__KismetSystemLibrary',
11
+ functionName: 'ExecuteConsoleCommand',
12
+ parameters: {
13
+ WorldContextObject: null,
14
+ Command: command,
15
+ SpecificPlayer: null
16
+ },
17
+ generateTransaction: false
18
+ });
19
+ }
20
+
21
+ // Create landscape with World Partition support (UE 5.6)
22
+ async createLandscape(params: {
23
+ name: string;
24
+ location?: [number, number, number];
25
+ sizeX?: number;
26
+ sizeY?: number;
27
+ quadsPerSection?: number;
28
+ sectionsPerComponent?: number;
29
+ componentCount?: number;
30
+ materialPath?: string;
31
+ // World Partition specific (UE 5.6)
32
+ enableWorldPartition?: boolean;
33
+ runtimeGrid?: string;
34
+ isSpatiallyLoaded?: boolean;
35
+ dataLayers?: string[];
36
+ }) {
37
+ // Try Python API with World Partition support for UE 5.6
38
+ try {
39
+ const pythonScript = `
40
+ import unreal
41
+ import json
42
+
43
+ # Get the editor world using the proper subsystem
44
+ try:
45
+ editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
46
+ world = editor_subsystem.get_editor_world() if hasattr(editor_subsystem, 'get_editor_world') else None
47
+ except:
48
+ # Fallback for older API
49
+ try:
50
+ world = unreal.EditorLevelLibrary.get_editor_world()
51
+ except:
52
+ world = None
53
+
54
+ is_world_partition = False
55
+ data_layer_manager = None
56
+
57
+ if world:
58
+ try:
59
+ # Check if World Partition is enabled (UE 5.6)
60
+ world_partition = world.get_world_partition()
61
+ is_world_partition = world_partition is not None
62
+ if is_world_partition:
63
+ # Get Data Layer Manager for UE 5.6
64
+ data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
65
+ except:
66
+ pass
67
+
68
+ # Try to create a basic landscape using available API
69
+ try:
70
+ # Use EditorLevelLibrary to spawn a landscape actor
71
+ location = unreal.Vector(${params.location?.[0] || 0}, ${params.location?.[1] || 0}, ${params.location?.[2] || 0})
72
+ rotation = unreal.Rotator(0, 0, 0)
73
+
74
+ # Check if LandscapeSubsystem is available (not LandscapeEditorSubsystem)
75
+ landscape_subsystem = None
76
+ try:
77
+ landscape_subsystem = unreal.get_editor_subsystem(unreal.LandscapeSubsystem)
78
+ except:
79
+ pass
80
+
81
+ if landscape_subsystem:
82
+ # Use subsystem if available
83
+ result = {"success": False, "error": "LandscapeSubsystem API limited via Python", "world_partition": is_world_partition}
84
+ else:
85
+ # Landscape actors cannot be properly spawned via Python API
86
+ # The component registration issues are inherent to how landscapes work
87
+ # Direct users to the proper workflow
88
+ result = {
89
+ "success": False,
90
+ "error": "Landscape creation is not supported via Python API",
91
+ "suggestion": "Please use Landscape Mode in the Unreal Editor toolbar:",
92
+ "steps": [
93
+ "1. Click 'Modes' dropdown in toolbar",
94
+ "2. Select 'Landscape'",
95
+ "3. Configure size and materials",
96
+ "4. Click 'Create' to generate landscape"
97
+ ],
98
+ "world_partition": is_world_partition
99
+ }
100
+
101
+ print(f'RESULT:{json.dumps(result)}')
102
+ except Exception as e:
103
+ print(f'RESULT:{{"success": false, "error": "{str(e)}"}}')
104
+ `.trim();
105
+
106
+ const response = await this.bridge.executePython(pythonScript);
107
+ const output = typeof response === 'string' ? response : JSON.stringify(response);
108
+ const match = output.match(/RESULT:({.*})/);
109
+
110
+ if (match) {
111
+ try {
112
+ const result = JSON.parse(match[1]);
113
+ if (result.world_partition) {
114
+ result.message = 'World Partition detected. Manual landscape creation required in editor.';
115
+ }
116
+ return result;
117
+ } catch {}
118
+ }
119
+ } catch {
120
+ // Continue to fallback
121
+ }
122
+
123
+ // Fallback message with World Partition info
124
+ return {
125
+ success: false,
126
+ error: 'Landscape creation via API is limited. Please use the Unreal Editor UI to create landscapes.',
127
+ worldPartitionSupport: params.enableWorldPartition ? 'Requested' : 'Not requested',
128
+ suggestion: 'Use the Landscape Mode in the editor toolbar to create and configure landscapes'
129
+ };
130
+ }
131
+
132
+ // Sculpt landscape
133
+ async sculptLandscape(_params: {
134
+ landscapeName: string;
135
+ tool: 'Sculpt' | 'Smooth' | 'Flatten' | 'Ramp' | 'Erosion' | 'Hydro' | 'Noise' | 'Retopologize';
136
+ brushSize?: number;
137
+ brushFalloff?: number;
138
+ strength?: number;
139
+ position?: [number, number, number];
140
+ }) {
141
+ return { success: false, error: 'sculptLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
142
+ }
143
+
144
+ // Paint landscape
145
+ async paintLandscape(_params: {
146
+ landscapeName: string;
147
+ layerName: string;
148
+ position: [number, number, number];
149
+ brushSize?: number;
150
+ strength?: number;
151
+ targetValue?: number;
152
+ }) {
153
+ return { success: false, error: 'paintLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
154
+ }
155
+
156
+ // Add landscape layer
157
+ async addLandscapeLayer(params: {
158
+ landscapeName: string;
159
+ layerName: string;
160
+ weightMapPath?: string;
161
+ blendMode?: 'Weight' | 'Alpha';
162
+ }) {
163
+ const commands = [];
164
+
165
+ commands.push(`AddLandscapeLayer ${params.landscapeName} ${params.layerName}`);
166
+
167
+ if (params.weightMapPath) {
168
+ commands.push(`SetLayerWeightMap ${params.layerName} ${params.weightMapPath}`);
169
+ }
170
+
171
+ if (params.blendMode) {
172
+ commands.push(`SetLayerBlendMode ${params.layerName} ${params.blendMode}`);
173
+ }
174
+
175
+ for (const cmd of commands) {
176
+ await this.bridge.executeConsoleCommand(cmd);
177
+ }
178
+
179
+ return { success: true, message: `Layer ${params.layerName} added to landscape` };
180
+ }
181
+
182
+ // Create landscape spline
183
+ async createLandscapeSpline(params: {
184
+ landscapeName: string;
185
+ splineName: string;
186
+ points: Array<[number, number, number]>;
187
+ width?: number;
188
+ falloffWidth?: number;
189
+ meshPath?: string;
190
+ }) {
191
+ const commands = [];
192
+
193
+ commands.push(`CreateLandscapeSpline ${params.landscapeName} ${params.splineName}`);
194
+
195
+ for (const point of params.points) {
196
+ commands.push(`AddSplinePoint ${params.splineName} ${point.join(' ')}`);
197
+ }
198
+
199
+ if (params.width !== undefined) {
200
+ commands.push(`SetSplineWidth ${params.splineName} ${params.width}`);
201
+ }
202
+
203
+ if (params.falloffWidth !== undefined) {
204
+ commands.push(`SetSplineFalloffWidth ${params.splineName} ${params.falloffWidth}`);
205
+ }
206
+
207
+ if (params.meshPath) {
208
+ commands.push(`SetSplineMesh ${params.splineName} ${params.meshPath}`);
209
+ }
210
+
211
+ for (const cmd of commands) {
212
+ await this.bridge.executeConsoleCommand(cmd);
213
+ }
214
+
215
+ return { success: true, message: `Landscape spline ${params.splineName} created` };
216
+ }
217
+
218
+ // Import heightmap
219
+ async importHeightmap(params: {
220
+ landscapeName: string;
221
+ heightmapPath: string;
222
+ scale?: [number, number, number];
223
+ }) {
224
+ const scale = params.scale || [100, 100, 100];
225
+ const command = `ImportLandscapeHeightmap ${params.landscapeName} ${params.heightmapPath} ${scale.join(' ')}`;
226
+
227
+ return this.bridge.executeConsoleCommand(command);
228
+ }
229
+
230
+ // Export heightmap
231
+ async exportHeightmap(params: {
232
+ landscapeName: string;
233
+ exportPath: string;
234
+ format?: 'PNG' | 'RAW';
235
+ }) {
236
+ const format = params.format || 'PNG';
237
+ const command = `ExportLandscapeHeightmap ${params.landscapeName} ${params.exportPath} ${format}`;
238
+
239
+ return this.bridge.executeConsoleCommand(command);
240
+ }
241
+
242
+ // Set landscape LOD
243
+ async setLandscapeLOD(params: {
244
+ landscapeName: string;
245
+ lodBias?: number;
246
+ forcedLOD?: number;
247
+ lodDistribution?: number;
248
+ }) {
249
+ const commands = [];
250
+
251
+ if (params.lodBias !== undefined) {
252
+ commands.push(`SetLandscapeLODBias ${params.landscapeName} ${params.lodBias}`);
253
+ }
254
+
255
+ if (params.forcedLOD !== undefined) {
256
+ commands.push(`SetLandscapeForcedLOD ${params.landscapeName} ${params.forcedLOD}`);
257
+ }
258
+
259
+ if (params.lodDistribution !== undefined) {
260
+ commands.push(`SetLandscapeLODDistribution ${params.landscapeName} ${params.lodDistribution}`);
261
+ }
262
+
263
+ for (const cmd of commands) {
264
+ await this.bridge.executeConsoleCommand(cmd);
265
+ }
266
+
267
+ return { success: true, message: 'Landscape LOD settings updated' };
268
+ }
269
+
270
+ // Create landscape grass
271
+ async createLandscapeGrass(params: {
272
+ landscapeName: string;
273
+ grassType: string;
274
+ density?: number;
275
+ minScale?: number;
276
+ maxScale?: number;
277
+ randomRotation?: boolean;
278
+ }) {
279
+ const commands = [];
280
+
281
+ commands.push(`CreateLandscapeGrass ${params.landscapeName} ${params.grassType}`);
282
+
283
+ if (params.density !== undefined) {
284
+ commands.push(`SetGrassDensity ${params.grassType} ${params.density}`);
285
+ }
286
+
287
+ if (params.minScale !== undefined && params.maxScale !== undefined) {
288
+ commands.push(`SetGrassScale ${params.grassType} ${params.minScale} ${params.maxScale}`);
289
+ }
290
+
291
+ if (params.randomRotation !== undefined) {
292
+ commands.push(`SetGrassRandomRotation ${params.grassType} ${params.randomRotation}`);
293
+ }
294
+
295
+ for (const cmd of commands) {
296
+ await this.bridge.executeConsoleCommand(cmd);
297
+ }
298
+
299
+ return { success: true, message: `Grass type ${params.grassType} created on landscape` };
300
+ }
301
+
302
+ // Landscape collision
303
+ async updateLandscapeCollision(params: {
304
+ landscapeName: string;
305
+ collisionMipLevel?: number;
306
+ simpleCollision?: boolean;
307
+ }) {
308
+ const commands = [];
309
+
310
+ if (params.collisionMipLevel !== undefined) {
311
+ commands.push(`SetLandscapeCollisionMipLevel ${params.landscapeName} ${params.collisionMipLevel}`);
312
+ }
313
+
314
+ if (params.simpleCollision !== undefined) {
315
+ commands.push(`SetLandscapeSimpleCollision ${params.landscapeName} ${params.simpleCollision}`);
316
+ }
317
+
318
+ commands.push(`UpdateLandscapeCollision ${params.landscapeName}`);
319
+
320
+ for (const cmd of commands) {
321
+ await this.bridge.executeConsoleCommand(cmd);
322
+ }
323
+
324
+ return { success: true, message: 'Landscape collision updated' };
325
+ }
326
+
327
+ // Retopologize landscape
328
+ async retopologizeLandscape(params: {
329
+ landscapeName: string;
330
+ targetTriangleCount?: number;
331
+ preserveDetails?: boolean;
332
+ }) {
333
+ const commands = [];
334
+
335
+ if (params.targetTriangleCount !== undefined) {
336
+ commands.push(`SetRetopologizeTarget ${params.targetTriangleCount}`);
337
+ }
338
+
339
+ if (params.preserveDetails !== undefined) {
340
+ commands.push(`SetRetopologizePreserveDetails ${params.preserveDetails}`);
341
+ }
342
+
343
+ commands.push(`RetopologizeLandscape ${params.landscapeName}`);
344
+
345
+ for (const cmd of commands) {
346
+ await this.bridge.executeConsoleCommand(cmd);
347
+ }
348
+
349
+ return { success: true, message: 'Landscape retopologized' };
350
+ }
351
+
352
+ // Create water body
353
+ async createWaterBody(params: {
354
+ type: 'Ocean' | 'Lake' | 'River' | 'Stream';
355
+ name: string;
356
+ location?: [number, number, number];
357
+ size?: [number, number];
358
+ depth?: number;
359
+ }) {
360
+ const loc = params.location || [0, 0, 0];
361
+ const size = params.size || [1000, 1000];
362
+ const depth = params.depth || 100;
363
+
364
+ const command = `CreateWaterBody ${params.type} ${params.name} ${loc.join(' ')} ${size.join(' ')} ${depth}`;
365
+
366
+ return this.bridge.executeConsoleCommand(command);
367
+ }
368
+
369
+ // World Partition support for landscapes (UE 5.6)
370
+ async configureWorldPartition(params: {
371
+ landscapeName: string;
372
+ enableSpatialLoading?: boolean;
373
+ runtimeGrid?: string;
374
+ dataLayers?: string[];
375
+ streamingDistance?: number;
376
+ }) {
377
+ try {
378
+ const pythonScript = `
379
+ import unreal
380
+
381
+ try:
382
+ # Get the landscape actor
383
+ actors = unreal.EditorLevelLibrary.get_all_level_actors()
384
+ landscape = None
385
+
386
+ for actor in actors:
387
+ if actor.get_name() == "${params.landscapeName}" or actor.get_actor_label() == "${params.landscapeName}":
388
+ if isinstance(actor, unreal.LandscapeProxy) or isinstance(actor, unreal.Landscape):
389
+ landscape = actor
390
+ break
391
+
392
+ if not landscape:
393
+ print('RESULT:{"success": false, "error": "Landscape not found"}')
394
+ else:
395
+ changes_made = []
396
+
397
+ # Configure spatial loading (UE 5.6)
398
+ if ${params.enableSpatialLoading !== undefined ? 'True' : 'False'}:
399
+ try:
400
+ landscape.set_editor_property('is_spatially_loaded', ${params.enableSpatialLoading || false})
401
+ changes_made.append("Spatial loading: ${params.enableSpatialLoading}")
402
+ except:
403
+ pass
404
+
405
+ # Set runtime grid (UE 5.6 World Partition)
406
+ if "${params.runtimeGrid || ''}":
407
+ try:
408
+ landscape.set_editor_property('runtime_grid', unreal.Name("${params.runtimeGrid}"))
409
+ changes_made.append("Runtime grid: ${params.runtimeGrid}")
410
+ except:
411
+ pass
412
+
413
+ # Configure data layers (UE 5.6)
414
+ if ${params.dataLayers ? 'True' : 'False'}:
415
+ try:
416
+ world = unreal.EditorLevelLibrary.get_editor_world()
417
+ data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
418
+ if data_layer_manager:
419
+ # Note: Full data layer API requires additional setup
420
+ changes_made.append("Data layers: Requires manual configuration")
421
+ except:
422
+ pass
423
+
424
+ if changes_made:
425
+ print('RESULT:{"success": true, "message": "World Partition configured", "changes": ' + str(changes_made).replace("'", '"') + '}')
426
+ else:
427
+ print('RESULT:{"success": false, "error": "No World Partition changes applied"}')
428
+
429
+ except Exception as e:
430
+ print(f'RESULT:{{"success": false, "error": "{str(e)}"}}')
431
+ `.trim();
432
+
433
+ const response = await this.bridge.executePython(pythonScript);
434
+ const output = typeof response === 'string' ? response : JSON.stringify(response);
435
+ const match = output.match(/RESULT:({.*})/);
436
+
437
+ if (match) {
438
+ try {
439
+ return JSON.parse(match[1]);
440
+ } catch {}
441
+ }
442
+
443
+ return { success: true, message: 'World Partition configuration attempted' };
444
+ } catch (err) {
445
+ return { success: false, error: `Failed to configure World Partition: ${err}` };
446
+ }
447
+ }
448
+
449
+ // Set landscape data layers (UE 5.6)
450
+ async setDataLayers(params: {
451
+ landscapeName: string;
452
+ dataLayerNames: string[];
453
+ operation: 'add' | 'remove' | 'set';
454
+ }) {
455
+ try {
456
+ const commands = [];
457
+
458
+ // Use console commands for data layer management
459
+ if (params.operation === 'set' || params.operation === 'add') {
460
+ for (const layerName of params.dataLayerNames) {
461
+ commands.push(`wp.Runtime.SetDataLayerRuntimeState Loaded ${layerName}`);
462
+ }
463
+ } else if (params.operation === 'remove') {
464
+ for (const layerName of params.dataLayerNames) {
465
+ commands.push(`wp.Runtime.SetDataLayerRuntimeState Unloaded ${layerName}`);
466
+ }
467
+ }
468
+
469
+ // Execute commands
470
+ for (const cmd of commands) {
471
+ await this.bridge.executeConsoleCommand(cmd);
472
+ }
473
+
474
+ return {
475
+ success: true,
476
+ message: `Data layers ${params.operation === 'add' ? 'added' : params.operation === 'remove' ? 'removed' : 'set'} for landscape`,
477
+ layers: params.dataLayerNames
478
+ };
479
+ } catch (err) {
480
+ return { success: false, error: `Failed to manage data layers: ${err}` };
481
+ }
482
+ }
483
+
484
+ // Configure landscape streaming cells (UE 5.6 World Partition)
485
+ async configureStreamingCells(params: {
486
+ landscapeName: string;
487
+ cellSize?: number;
488
+ loadingRange?: number;
489
+ enableHLOD?: boolean;
490
+ }) {
491
+ const commands = [];
492
+
493
+ // World Partition runtime commands
494
+ if (params.loadingRange !== undefined) {
495
+ commands.push(`wp.Runtime.OverrideRuntimeSpatialHashLoadingRange -grid=0 -range=${params.loadingRange}`);
496
+ }
497
+
498
+ if (params.enableHLOD !== undefined) {
499
+ commands.push(`wp.Runtime.HLOD ${params.enableHLOD ? '1' : '0'}`);
500
+ }
501
+
502
+ // Debug visualization commands
503
+ commands.push('wp.Runtime.ToggleDrawRuntimeHash2D'); // Show 2D grid
504
+
505
+ try {
506
+ for (const cmd of commands) {
507
+ await this.bridge.executeConsoleCommand(cmd);
508
+ }
509
+
510
+ return {
511
+ success: true,
512
+ message: 'Streaming cells configured for World Partition',
513
+ settings: {
514
+ cellSize: params.cellSize,
515
+ loadingRange: params.loadingRange,
516
+ hlod: params.enableHLOD
517
+ }
518
+ };
519
+ } catch (err) {
520
+ return { success: false, error: `Failed to configure streaming cells: ${err}` };
521
+ }
522
+ }
523
+ }