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,549 @@
1
+ // Performance tools for Unreal Engine
2
+ import { UnrealBridge } from '../unreal-bridge.js';
3
+
4
+ export class PerformanceTools {
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
+ // Start profiling
22
+ async startProfiling(params: {
23
+ type: 'CPU' | 'GPU' | 'Memory' | 'RenderThread' | 'GameThread' | 'All';
24
+ duration?: number;
25
+ }) {
26
+ const commands = [];
27
+
28
+ switch (params.type) {
29
+ case 'CPU':
30
+ commands.push('stat startfile');
31
+ break;
32
+ case 'GPU':
33
+ commands.push('profilegpu');
34
+ break;
35
+ case 'Memory':
36
+ commands.push('stat memory');
37
+ break;
38
+ case 'RenderThread':
39
+ commands.push('stat renderthread');
40
+ break;
41
+ case 'GameThread':
42
+ commands.push('stat game');
43
+ break;
44
+ case 'All':
45
+ commands.push('stat startfile');
46
+ commands.push('profilegpu');
47
+ commands.push('stat memory');
48
+ break;
49
+ }
50
+
51
+ if (params.duration) {
52
+ commands.push(`stat stopfile ${params.duration}`);
53
+ }
54
+
55
+ for (const cmd of commands) {
56
+ await this.bridge.executeConsoleCommand(cmd);
57
+ }
58
+
59
+ return { success: true, message: `${params.type} profiling started` };
60
+ }
61
+
62
+ // Stop profiling
63
+ async stopProfiling() {
64
+ const commands = [
65
+ 'stat stopfile',
66
+ 'stat none'
67
+ ];
68
+
69
+ for (const cmd of commands) {
70
+ await this.bridge.executeConsoleCommand(cmd);
71
+ }
72
+
73
+ return { success: true, message: 'Profiling stopped' };
74
+ }
75
+
76
+ // Show FPS
77
+ async showFPS(params: {
78
+ enabled: boolean;
79
+ verbose?: boolean;
80
+ }) {
81
+ const startTime = Date.now();
82
+ console.log('[PerformanceTools] Starting showFPS with params:', params);
83
+
84
+ try {
85
+ // Use stat fps as requested - shows FPS counter
86
+ // For more detailed timing info, use 'stat unit' instead
87
+ const command = params.enabled
88
+ ? (params.verbose ? 'stat unit' : 'stat fps')
89
+ : 'stat none';
90
+
91
+ console.log(`[PerformanceTools] Executing command: ${command}`);
92
+ await this.bridge.executeConsoleCommand(command);
93
+ console.log(`[PerformanceTools] Command completed in ${Date.now() - startTime}ms`);
94
+ return {
95
+ success: true,
96
+ message: params.enabled ? 'FPS display enabled' : 'FPS display disabled',
97
+ fpsVisible: params.enabled,
98
+ command: command
99
+ };
100
+ } catch (error) {
101
+ return {
102
+ success: false,
103
+ error: `Failed to ${params.enabled ? 'enable' : 'disable'} FPS display: ${error}`,
104
+ fpsVisible: false
105
+ };
106
+ }
107
+ }
108
+
109
+ // Show performance stats
110
+ async showStats(params: {
111
+ category: 'Unit' | 'FPS' | 'Memory' | 'Game' | 'Slate' | 'Engine' | 'RHI' | 'Streaming' | 'SceneRendering' | 'Physics' | 'Navigation' | 'Particles' | 'Audio';
112
+ enabled: boolean;
113
+ }) {
114
+ const command = params.enabled
115
+ ? `stat ${params.category.toLowerCase()}`
116
+ : 'stat none';
117
+
118
+ return this.bridge.executeConsoleCommand(command);
119
+ }
120
+
121
+ // Set scalability settings using console commands
122
+ async setScalability(params: {
123
+ category: 'ViewDistance' | 'AntiAliasing' | 'PostProcessing' | 'PostProcess' | 'Shadows' | 'GlobalIllumination' | 'Reflections' | 'Textures' | 'Effects' | 'Foliage' | 'Shading';
124
+ level: 0 | 1 | 2 | 3 | 4; // 0=Low, 1=Medium, 2=High, 3=Epic, 4=Cinematic
125
+ }) {
126
+ // Map incoming category to the base name expected by "sg.<Base>Quality"
127
+ // Note: Several CVars use singular form (Shadow/Texture/Reflection)
128
+ const categoryBaseMap: Record<string, string> = {
129
+ ViewDistance: 'ViewDistance',
130
+ AntiAliasing: 'AntiAliasing',
131
+ PostProcessing: 'PostProcess',
132
+ PostProcess: 'PostProcess',
133
+ Shadows: 'Shadow',
134
+ GlobalIllumination: 'GlobalIllumination',
135
+ Reflections: 'Reflection',
136
+ Textures: 'Texture',
137
+ Effects: 'Effects',
138
+ Foliage: 'Foliage',
139
+ Shading: 'Shading',
140
+ };
141
+
142
+ const base = categoryBaseMap[params.category] || params.category;
143
+
144
+ // Use direct console command to set with highest priority (SetByConsole)
145
+ // This avoids conflicts with the scalability system
146
+ const setCommand = `sg.${base}Quality ${params.level}`;
147
+
148
+ // Apply the console command directly
149
+ await this.bridge.executeConsoleCommand(setCommand);
150
+
151
+ // Skip GameUserSettings entirely to avoid any scalability triggers
152
+ // Console command already applied with correct priority
153
+ /* eslint-disable no-useless-escape */
154
+ const py = `
155
+ import unreal, json
156
+ result = {'success': True, 'category': '${base}', 'requested': ${params.level}, 'actual': ${params.level}, 'method': 'ConsoleOnly'}
157
+
158
+ # Simply verify the console variable was set correctly
159
+ try:
160
+ # Try to read the console variable directly to verify it was set
161
+ # This doesn't trigger any scalability system
162
+ import sys
163
+ from io import StringIO
164
+
165
+ # Capture console output
166
+ old_stdout = sys.stdout
167
+ sys.stdout = StringIO()
168
+
169
+ # Execute console command to query the value
170
+ try:
171
+ unreal.SystemLibrary.execute_console_command(None, 'sg.${base}Quality', None)
172
+ except:
173
+ pass
174
+
175
+ # Get the output
176
+ console_output = sys.stdout.getvalue()
177
+ sys.stdout = old_stdout
178
+
179
+ # Parse the output to get the actual value
180
+ if 'sg.${base}Quality' in console_output:
181
+ # Extract the value from output like 'sg.ShadowQuality = "3"'
182
+ import re
183
+ match = re.search(r'sg\.${base}Quality\\s*=\\s*"(\\d+)"', console_output)
184
+ if match:
185
+ result['actual'] = int(match.group(1))
186
+ result['verified'] = True
187
+
188
+ result['method'] = 'ConsoleOnly'
189
+ except Exception as e:
190
+ # Even on error, the console command was applied
191
+ result['method'] = 'ConsoleOnly'
192
+ result['note'] = str(e)
193
+
194
+ print('RESULT:' + json.dumps(result))
195
+ `.trim();
196
+ /* eslint-enable no-useless-escape */
197
+
198
+ // Always try to apply through Python for consistency
199
+ try {
200
+ const pyResp = await this.bridge.executePython(py);
201
+ let out = '';
202
+ if (pyResp?.LogOutput && Array.isArray(pyResp.LogOutput)) {
203
+ out = pyResp.LogOutput.map((l: any) => l.Output || '').join('');
204
+ } else if (typeof pyResp === 'string') {
205
+ out = pyResp;
206
+ } else {
207
+ out = JSON.stringify(pyResp);
208
+ }
209
+
210
+ const m = out.match(/RESULT:({.*})/);
211
+ if (m) {
212
+ try {
213
+ const parsed = JSON.parse(m[1]);
214
+ const verified = parsed.success && (parsed.actual === params.level);
215
+ return {
216
+ success: true,
217
+ message: `${params.category} quality set to level ${params.level}`,
218
+ verified,
219
+ readback: parsed.actual,
220
+ method: parsed.method || 'Unknown'
221
+ };
222
+ } catch {
223
+ // Fall through to simple success
224
+ }
225
+ }
226
+ } catch {
227
+ // Ignore Python errors and fall through
228
+ }
229
+
230
+ // If Python fails, the console command was still applied
231
+ return {
232
+ success: true,
233
+ message: `${params.category} quality set to level ${params.level}`,
234
+ method: 'CVarOnly'
235
+ };
236
+ }
237
+
238
+ // Set resolution scale
239
+ async setResolutionScale(params: {
240
+ scale: number; // 0.5 to 2.0
241
+ }) {
242
+ // Validate input
243
+ if (params.scale === undefined || params.scale === null || isNaN(params.scale)) {
244
+ return { success: false, error: 'Invalid scale parameter' };
245
+ }
246
+
247
+ // Clamp scale between 10% (0.1) and 200% (2.0) - Unreal Engine limits
248
+ // Note: r.ScreenPercentage takes values from 10 to 200, not 0.5 to 2.0
249
+ const clampedScale = Math.max(0.1, Math.min(2.0, params.scale));
250
+ const percentage = Math.round(clampedScale * 100);
251
+
252
+ // Ensure percentage is within Unreal's valid range
253
+ const finalPercentage = Math.max(10, Math.min(200, percentage));
254
+
255
+ const command = `r.ScreenPercentage ${finalPercentage}`;
256
+
257
+ try {
258
+ await this.bridge.executeConsoleCommand(command);
259
+ return {
260
+ success: true,
261
+ message: `Resolution scale set to ${finalPercentage}%`,
262
+ actualScale: finalPercentage / 100
263
+ };
264
+ } catch (e) {
265
+ return { success: false, error: `Failed to set resolution scale: ${e}` };
266
+ }
267
+ }
268
+
269
+ // Enable/disable vsync
270
+ async setVSync(params: {
271
+ enabled: boolean;
272
+ }) {
273
+ const command = `r.VSync ${params.enabled ? 1 : 0}`;
274
+ return this.bridge.executeConsoleCommand(command);
275
+ }
276
+
277
+ // Set frame rate limit
278
+ async setFrameRateLimit(params: {
279
+ maxFPS: number; // 0 for unlimited
280
+ }) {
281
+ const command = `t.MaxFPS ${params.maxFPS}`;
282
+ return this.bridge.executeConsoleCommand(command);
283
+ }
284
+
285
+ // Enable GPU timing
286
+ async enableGPUTiming(params: {
287
+ enabled: boolean;
288
+ }) {
289
+ const command = `r.GPUStatsEnabled ${params.enabled ? 1 : 0}`;
290
+ return this.bridge.executeConsoleCommand(command);
291
+ }
292
+
293
+ // Memory report
294
+ async generateMemoryReport(params: {
295
+ detailed?: boolean;
296
+ outputPath?: string;
297
+ }) {
298
+ const commands = [];
299
+
300
+ if (params.detailed) {
301
+ commands.push('memreport -full');
302
+ } else {
303
+ commands.push('memreport');
304
+ }
305
+
306
+ if (params.outputPath) {
307
+ commands.push(`obj savepackage ${params.outputPath}`);
308
+ }
309
+
310
+ for (const cmd of commands) {
311
+ await this.bridge.executeConsoleCommand(cmd);
312
+ }
313
+
314
+ return { success: true, message: 'Memory report generated' };
315
+ }
316
+
317
+ // Texture streaming
318
+ async configureTextureStreaming(params: {
319
+ enabled: boolean;
320
+ poolSize?: number; // MB
321
+ boostPlayerLocation?: boolean;
322
+ }) {
323
+ const commands = [];
324
+
325
+ commands.push(`r.TextureStreaming ${params.enabled ? 1 : 0}`);
326
+
327
+ if (params.poolSize !== undefined) {
328
+ commands.push(`r.Streaming.PoolSize ${params.poolSize}`);
329
+ }
330
+
331
+ if (params.boostPlayerLocation !== undefined) {
332
+ commands.push(`r.Streaming.UseFixedPoolSize ${params.boostPlayerLocation ? 1 : 0}`);
333
+ }
334
+
335
+ for (const cmd of commands) {
336
+ await this.bridge.executeConsoleCommand(cmd);
337
+ }
338
+
339
+ return { success: true, message: 'Texture streaming configured' };
340
+ }
341
+
342
+ // LOD settings
343
+ async configureLOD(params: {
344
+ forceLOD?: number;
345
+ lodBias?: number; // skeletal LOD bias (int)
346
+ distanceScale?: number; // distance scale (float) applied to both static and skeletal
347
+ }) {
348
+ const commands = [];
349
+
350
+ if (params.forceLOD !== undefined) {
351
+ commands.push(`r.ForceLOD ${params.forceLOD}`);
352
+ }
353
+
354
+ if (params.lodBias !== undefined) {
355
+ // Skeletal mesh LOD bias is an integer bias value
356
+ commands.push(`r.SkeletalMeshLODBias ${params.lodBias}`);
357
+ }
358
+
359
+ if (params.distanceScale !== undefined) {
360
+ // Apply distance scale to both static and skeletal meshes
361
+ commands.push(`r.StaticMeshLODDistanceScale ${params.distanceScale}`);
362
+ commands.push(`r.SkeletalMeshLODDistanceScale ${params.distanceScale}`);
363
+ }
364
+
365
+ for (const cmd of commands) {
366
+ await this.bridge.executeConsoleCommand(cmd);
367
+ }
368
+
369
+ return { success: true, message: 'LOD settings configured' };
370
+ }
371
+
372
+ // Apply a baseline performance profile (explicit CVar enforcement)
373
+ async applyBaselinePerformanceSettings(params?: {
374
+ distanceScale?: number; // default 1.0
375
+ skeletalBias?: number; // default 0
376
+ vsync?: boolean; // default false
377
+ maxFPS?: number; // default 60
378
+ hzb?: boolean; // default true
379
+ }) {
380
+ const p = {
381
+ distanceScale: params?.distanceScale ?? 1.0,
382
+ skeletalBias: params?.skeletalBias ?? 0,
383
+ vsync: params?.vsync ?? false,
384
+ maxFPS: params?.maxFPS ?? 60,
385
+ hzb: params?.hzb ?? true,
386
+ };
387
+
388
+ const commands = [
389
+ `r.StaticMeshLODDistanceScale ${p.distanceScale}`,
390
+ `r.SkeletalMeshLODDistanceScale ${p.distanceScale}`,
391
+ `r.SkeletalMeshLODBias ${p.skeletalBias}`,
392
+ `r.HZBOcclusion ${p.hzb ? 1 : 0}`,
393
+ `r.VSync ${p.vsync ? 1 : 0}`,
394
+ `t.MaxFPS ${p.maxFPS}`,
395
+ ];
396
+
397
+ for (const cmd of commands) {
398
+ await this.bridge.executeConsoleCommand(cmd);
399
+ }
400
+
401
+ return { success: true, message: 'Baseline performance settings applied', params: p };
402
+ }
403
+
404
+ // Draw call optimization
405
+ async optimizeDrawCalls(params: {
406
+ enableInstancing?: boolean;
407
+ enableBatching?: boolean; // no-op (deprecated internal toggle)
408
+ mergeActors?: boolean;
409
+ }) {
410
+ const commands = [];
411
+
412
+ if (params.enableInstancing !== undefined) {
413
+ commands.push(`r.MeshDrawCommands.DynamicInstancing ${params.enableInstancing ? 1 : 0}`);
414
+ }
415
+
416
+ // Avoid using r.RHICmdBypass; it's a low-level debug toggle and not suitable for general batching control
417
+
418
+ if (params.mergeActors) {
419
+ commands.push('MergeActors');
420
+ }
421
+
422
+ for (const cmd of commands) {
423
+ await this.bridge.executeConsoleCommand(cmd);
424
+ }
425
+
426
+ return { success: true, message: 'Draw call optimization configured' };
427
+ }
428
+
429
+ // Occlusion culling
430
+ async configureOcclusionCulling(params: {
431
+ enabled: boolean;
432
+ method?: 'Hardware' | 'Software' | 'Hierarchical';
433
+ freezeRendering?: boolean;
434
+ }) {
435
+ const commands = [];
436
+
437
+ // Enable/disable HZB occlusion (boolean)
438
+ commands.push(`r.HZBOcclusion ${params.enabled ? 1 : 0}`);
439
+
440
+ // Optional freeze rendering toggle
441
+ if (params.freezeRendering !== undefined) {
442
+ commands.push(`FreezeRendering ${params.freezeRendering ? 1 : 0}`);
443
+ }
444
+
445
+ for (const cmd of commands) {
446
+ await this.bridge.executeConsoleCommand(cmd);
447
+ }
448
+
449
+ return { success: true, message: 'Occlusion culling configured' };
450
+ }
451
+
452
+ // Shader compilation
453
+ async optimizeShaders(params: {
454
+ compileOnDemand?: boolean;
455
+ cacheShaders?: boolean;
456
+ reducePermutations?: boolean;
457
+ }) {
458
+ const commands = [];
459
+
460
+ if (params.compileOnDemand !== undefined) {
461
+ commands.push(`r.ShaderDevelopmentMode ${params.compileOnDemand ? 1 : 0}`);
462
+ }
463
+
464
+ if (params.cacheShaders !== undefined) {
465
+ commands.push(`r.ShaderPipelineCache.Enabled ${params.cacheShaders ? 1 : 0}`);
466
+ }
467
+
468
+ if (params.reducePermutations) {
469
+ commands.push('RecompileShaders changed');
470
+ }
471
+
472
+ for (const cmd of commands) {
473
+ await this.bridge.executeConsoleCommand(cmd);
474
+ }
475
+
476
+ return { success: true, message: 'Shader optimization configured' };
477
+ }
478
+
479
+ // Nanite settings
480
+ async configureNanite(params: {
481
+ enabled: boolean;
482
+ maxPixelsPerEdge?: number;
483
+ streamingPoolSize?: number;
484
+ }) {
485
+ const commands = [];
486
+
487
+ commands.push(`r.Nanite ${params.enabled ? 1 : 0}`);
488
+
489
+ if (params.maxPixelsPerEdge !== undefined) {
490
+ commands.push(`r.Nanite.MaxPixelsPerEdge ${params.maxPixelsPerEdge}`);
491
+ }
492
+
493
+ if (params.streamingPoolSize !== undefined) {
494
+ commands.push(`r.Nanite.StreamingPoolSize ${params.streamingPoolSize}`);
495
+ }
496
+
497
+ for (const cmd of commands) {
498
+ await this.bridge.executeConsoleCommand(cmd);
499
+ }
500
+
501
+ return { success: true, message: 'Nanite configured' };
502
+ }
503
+
504
+ // World Partition streaming
505
+ async configureWorldPartition(params: {
506
+ enabled: boolean;
507
+ streamingDistance?: number;
508
+ cellSize?: number;
509
+ }) {
510
+ const commands = [];
511
+
512
+ commands.push(`wp.Runtime.EnableStreaming ${params.enabled ? 1 : 0}`);
513
+
514
+ if (params.streamingDistance !== undefined) {
515
+ commands.push(`wp.Runtime.StreamingDistance ${params.streamingDistance}`);
516
+ }
517
+
518
+ if (params.cellSize !== undefined) {
519
+ commands.push(`wp.Runtime.CellSize ${params.cellSize}`);
520
+ }
521
+
522
+ for (const cmd of commands) {
523
+ await this.bridge.executeConsoleCommand(cmd);
524
+ }
525
+
526
+ return { success: true, message: 'World Partition configured' };
527
+ }
528
+
529
+ // Benchmark
530
+ async runBenchmark(params: {
531
+ duration?: number;
532
+ outputPath?: string;
533
+ }) {
534
+ const duration = params.duration || 60;
535
+
536
+ // Start recording and GPU profiling
537
+ await this.bridge.executeConsoleCommand('stat startfile');
538
+ await this.bridge.executeConsoleCommand('profilegpu');
539
+
540
+ // Wait for the requested duration
541
+ await new Promise(resolve => setTimeout(resolve, duration * 1000));
542
+
543
+ // Stop recording and clear stats
544
+ await this.bridge.executeConsoleCommand('stat stopfile');
545
+ await this.bridge.executeConsoleCommand('stat none');
546
+
547
+ return { success: true, message: `Benchmark completed for ${duration} seconds` };
548
+ }
549
+ }