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