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,171 @@
1
+ import { UnrealBridge } from '../unreal-bridge.js';
2
+ export declare class UITools {
3
+ private bridge;
4
+ constructor(bridge: UnrealBridge);
5
+ private _executeCommand;
6
+ createWidget(params: {
7
+ name: string;
8
+ type?: 'HUD' | 'Menu' | 'Inventory' | 'Dialog' | 'Custom';
9
+ savePath?: string;
10
+ }): Promise<{
11
+ success: boolean;
12
+ message: string;
13
+ error?: undefined;
14
+ } | {
15
+ success: boolean;
16
+ error: any;
17
+ message?: undefined;
18
+ }>;
19
+ addWidgetComponent(params: {
20
+ widgetName: string;
21
+ componentType: 'Button' | 'Text' | 'Image' | 'ProgressBar' | 'Slider' | 'CheckBox' | 'ComboBox' | 'TextBox' | 'ScrollBox' | 'Canvas' | 'VerticalBox' | 'HorizontalBox' | 'Grid' | 'Overlay';
22
+ componentName: string;
23
+ slot?: {
24
+ position?: [number, number];
25
+ size?: [number, number];
26
+ anchor?: [number, number, number, number];
27
+ alignment?: [number, number];
28
+ };
29
+ }): Promise<{
30
+ success: boolean;
31
+ message: string;
32
+ }>;
33
+ setWidgetText(params: {
34
+ widgetName: string;
35
+ componentName: string;
36
+ text: string;
37
+ fontSize?: number;
38
+ color?: [number, number, number, number];
39
+ fontFamily?: string;
40
+ }): Promise<{
41
+ success: boolean;
42
+ message: string;
43
+ }>;
44
+ setWidgetImage(params: {
45
+ widgetName: string;
46
+ componentName: string;
47
+ imagePath: string;
48
+ tint?: [number, number, number, number];
49
+ sizeToContent?: boolean;
50
+ }): Promise<{
51
+ success: boolean;
52
+ message: string;
53
+ }>;
54
+ createHUD(params: {
55
+ name: string;
56
+ elements?: Array<{
57
+ type: 'HealthBar' | 'AmmoCounter' | 'Score' | 'Timer' | 'Minimap' | 'Crosshair';
58
+ position: [number, number];
59
+ size?: [number, number];
60
+ }>;
61
+ }): Promise<{
62
+ success: boolean;
63
+ message: string;
64
+ }>;
65
+ setWidgetVisibility(params: {
66
+ widgetName: string;
67
+ visible: boolean;
68
+ playerIndex?: number;
69
+ }): Promise<any>;
70
+ addWidgetToViewport(params: {
71
+ widgetClass: string;
72
+ zOrder?: number;
73
+ playerIndex?: number;
74
+ }): Promise<{
75
+ success: boolean;
76
+ message: string;
77
+ error?: undefined;
78
+ } | {
79
+ success: boolean;
80
+ error: any;
81
+ message?: undefined;
82
+ }>;
83
+ removeWidgetFromViewport(params: {
84
+ widgetName: string;
85
+ playerIndex?: number;
86
+ }): Promise<any>;
87
+ createMenu(params: {
88
+ name: string;
89
+ menuType: 'Main' | 'Pause' | 'Settings' | 'Inventory';
90
+ buttons?: Array<{
91
+ text: string;
92
+ action: string;
93
+ position?: [number, number];
94
+ }>;
95
+ }): Promise<{
96
+ success: boolean;
97
+ message: string;
98
+ }>;
99
+ createWidgetAnimation(params: {
100
+ widgetName: string;
101
+ animationName: string;
102
+ duration: number;
103
+ tracks?: Array<{
104
+ componentName: string;
105
+ property: 'Position' | 'Scale' | 'Rotation' | 'Opacity' | 'Color';
106
+ keyframes: Array<{
107
+ time: number;
108
+ value: number | [number, number] | [number, number, number] | [number, number, number, number];
109
+ }>;
110
+ }>;
111
+ }): Promise<{
112
+ success: boolean;
113
+ message: string;
114
+ }>;
115
+ playWidgetAnimation(params: {
116
+ widgetName: string;
117
+ animationName: string;
118
+ playMode?: 'Forward' | 'Reverse' | 'PingPong';
119
+ loops?: number;
120
+ }): Promise<any>;
121
+ setWidgetStyle(params: {
122
+ widgetName: string;
123
+ componentName: string;
124
+ style: {
125
+ backgroundColor?: [number, number, number, number];
126
+ borderColor?: [number, number, number, number];
127
+ borderWidth?: number;
128
+ padding?: [number, number, number, number];
129
+ margin?: [number, number, number, number];
130
+ };
131
+ }): Promise<{
132
+ success: boolean;
133
+ message: string;
134
+ }>;
135
+ bindWidgetEvent(params: {
136
+ widgetName: string;
137
+ componentName: string;
138
+ eventType: 'OnClicked' | 'OnPressed' | 'OnReleased' | 'OnHovered' | 'OnUnhovered' | 'OnTextChanged' | 'OnTextCommitted' | 'OnValueChanged';
139
+ functionName: string;
140
+ }): Promise<any>;
141
+ setInputMode(params: {
142
+ mode: 'GameOnly' | 'UIOnly' | 'GameAndUI';
143
+ showCursor?: boolean;
144
+ lockCursor?: boolean;
145
+ }): Promise<{
146
+ success: boolean;
147
+ message: string;
148
+ }>;
149
+ createTooltip(params: {
150
+ widgetName: string;
151
+ componentName: string;
152
+ text: string;
153
+ delay?: number;
154
+ }): Promise<any>;
155
+ setupDragDrop(params: {
156
+ widgetName: string;
157
+ componentName: string;
158
+ dragVisual?: string;
159
+ dropTargets?: string[];
160
+ }): Promise<{
161
+ success: boolean;
162
+ message: string;
163
+ }>;
164
+ showNotification(params: {
165
+ text: string;
166
+ duration?: number;
167
+ type?: 'Info' | 'Success' | 'Warning' | 'Error';
168
+ position?: 'TopLeft' | 'TopCenter' | 'TopRight' | 'BottomLeft' | 'BottomCenter' | 'BottomRight';
169
+ }): Promise<any>;
170
+ }
171
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1,337 @@
1
+ export class UITools {
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
+ Command: command,
13
+ SpecificPlayer: null
14
+ },
15
+ generateTransaction: false
16
+ });
17
+ }
18
+ // Create widget blueprint
19
+ async createWidget(params) {
20
+ const path = params.savePath || '/Game/UI/Widgets';
21
+ const py = `
22
+ import unreal
23
+ import json
24
+ name = r"${params.name}"
25
+ path = r"${path}"
26
+ try:
27
+ asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
28
+ try:
29
+ factory = unreal.WidgetBlueprintFactory()
30
+ except Exception:
31
+ factory = None
32
+ if not factory:
33
+ print('RESULT:' + json.dumps({'success': False, 'error': 'WidgetBlueprintFactory unavailable'}))
34
+ else:
35
+ # Try setting parent_class in a version-tolerant way
36
+ try:
37
+ factory.parent_class = unreal.UserWidget
38
+ except Exception:
39
+ try:
40
+ factory.set_editor_property('parent_class', unreal.UserWidget)
41
+ except Exception:
42
+ pass
43
+ asset = asset_tools.create_asset(asset_name=name, package_path=path, asset_class=unreal.WidgetBlueprint, factory=factory)
44
+ if asset:
45
+ unreal.EditorAssetLibrary.save_asset(f"{path}/{name}")
46
+ print('RESULT:' + json.dumps({'success': True}))
47
+ else:
48
+ print('RESULT:' + json.dumps({'success': False, 'error': 'Failed to create WidgetBlueprint'}))
49
+ except Exception as e:
50
+ print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
51
+ `.trim();
52
+ try {
53
+ const resp = await this.bridge.executePython(py);
54
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
55
+ const m = out.match(/RESULT:({.*})/);
56
+ if (m) {
57
+ try {
58
+ const parsed = JSON.parse(m[1]);
59
+ return parsed.success ? { success: true, message: 'Widget blueprint created' } : { success: false, error: parsed.error };
60
+ }
61
+ catch { }
62
+ }
63
+ return { success: true, message: 'Widget blueprint creation attempted' };
64
+ }
65
+ catch (e) {
66
+ return { success: false, error: `Failed to create widget blueprint: ${e}` };
67
+ }
68
+ }
69
+ // Add widget component
70
+ async addWidgetComponent(params) {
71
+ const commands = [];
72
+ commands.push(`AddWidgetComponent ${params.widgetName} ${params.componentType} ${params.componentName}`);
73
+ if (params.slot) {
74
+ if (params.slot.position) {
75
+ commands.push(`SetWidgetPosition ${params.widgetName}.${params.componentName} ${params.slot.position.join(' ')}`);
76
+ }
77
+ if (params.slot.size) {
78
+ commands.push(`SetWidgetSize ${params.widgetName}.${params.componentName} ${params.slot.size.join(' ')}`);
79
+ }
80
+ if (params.slot.anchor) {
81
+ commands.push(`SetWidgetAnchor ${params.widgetName}.${params.componentName} ${params.slot.anchor.join(' ')}`);
82
+ }
83
+ if (params.slot.alignment) {
84
+ commands.push(`SetWidgetAlignment ${params.widgetName}.${params.componentName} ${params.slot.alignment.join(' ')}`);
85
+ }
86
+ }
87
+ for (const cmd of commands) {
88
+ await this.bridge.executeConsoleCommand(cmd);
89
+ }
90
+ return { success: true, message: `Component ${params.componentName} added to widget` };
91
+ }
92
+ // Set text
93
+ async setWidgetText(params) {
94
+ const commands = [];
95
+ commands.push(`SetWidgetText ${params.widgetName}.${params.componentName} "${params.text}"`);
96
+ if (params.fontSize !== undefined) {
97
+ commands.push(`SetWidgetFontSize ${params.widgetName}.${params.componentName} ${params.fontSize}`);
98
+ }
99
+ if (params.color) {
100
+ commands.push(`SetWidgetTextColor ${params.widgetName}.${params.componentName} ${params.color.join(' ')}`);
101
+ }
102
+ if (params.fontFamily) {
103
+ commands.push(`SetWidgetFont ${params.widgetName}.${params.componentName} ${params.fontFamily}`);
104
+ }
105
+ for (const cmd of commands) {
106
+ await this.bridge.executeConsoleCommand(cmd);
107
+ }
108
+ return { success: true, message: 'Widget text updated' };
109
+ }
110
+ // Set image
111
+ async setWidgetImage(params) {
112
+ const commands = [];
113
+ commands.push(`SetWidgetImage ${params.widgetName}.${params.componentName} ${params.imagePath}`);
114
+ if (params.tint) {
115
+ commands.push(`SetWidgetImageTint ${params.widgetName}.${params.componentName} ${params.tint.join(' ')}`);
116
+ }
117
+ if (params.sizeToContent !== undefined) {
118
+ commands.push(`SetWidgetSizeToContent ${params.widgetName}.${params.componentName} ${params.sizeToContent}`);
119
+ }
120
+ for (const cmd of commands) {
121
+ await this.bridge.executeConsoleCommand(cmd);
122
+ }
123
+ return { success: true, message: 'Widget image updated' };
124
+ }
125
+ // Create HUD
126
+ async createHUD(params) {
127
+ const commands = [];
128
+ commands.push(`CreateHUDClass ${params.name}`);
129
+ if (params.elements) {
130
+ for (const element of params.elements) {
131
+ const size = element.size || [100, 50];
132
+ commands.push(`AddHUDElement ${params.name} ${element.type} ${element.position.join(' ')} ${size.join(' ')}`);
133
+ }
134
+ }
135
+ for (const cmd of commands) {
136
+ await this.bridge.executeConsoleCommand(cmd);
137
+ }
138
+ return { success: true, message: `HUD ${params.name} created` };
139
+ }
140
+ // Show/Hide widget
141
+ async setWidgetVisibility(params) {
142
+ const playerIndex = params.playerIndex ?? 0;
143
+ const command = params.visible
144
+ ? `ShowWidget ${params.widgetName} ${playerIndex}`
145
+ : `HideWidget ${params.widgetName} ${playerIndex}`;
146
+ return this.bridge.executeConsoleCommand(command);
147
+ }
148
+ // Add widget to viewport
149
+ async addWidgetToViewport(params) {
150
+ const zOrder = params.zOrder ?? 0;
151
+ const playerIndex = params.playerIndex ?? 0;
152
+ // Use Python API to create and add widget to viewport
153
+ const py = `
154
+ import unreal
155
+ import json
156
+ widget_path = r"${params.widgetClass}"
157
+ z_order = ${zOrder}
158
+ player_index = ${playerIndex}
159
+ try:
160
+ # Load the widget blueprint class
161
+ if not unreal.EditorAssetLibrary.does_asset_exist(widget_path):
162
+ print('RESULT:' + json.dumps({'success': False, 'error': f'Widget class not found: {widget_path}'}))
163
+ else:
164
+ widget_bp = unreal.EditorAssetLibrary.load_asset(widget_path)
165
+ if not widget_bp:
166
+ print('RESULT:' + json.dumps({'success': False, 'error': 'Failed to load widget blueprint'}))
167
+ else:
168
+ # Get the generated class from the widget blueprint
169
+ widget_class = widget_bp.generated_class() if hasattr(widget_bp, 'generated_class') else widget_bp
170
+
171
+ # Get the world and player controller
172
+ world = unreal.EditorLevelLibrary.get_editor_world()
173
+ if not world:
174
+ print('RESULT:' + json.dumps({'success': False, 'error': 'No editor world available'}))
175
+ else:
176
+ # Try to get player controller
177
+ try:
178
+ player_controller = unreal.GameplayStatics.get_player_controller(world, player_index)
179
+ except Exception:
180
+ player_controller = None
181
+
182
+ if not player_controller:
183
+ # If no player controller in PIE, try to get the first one or create a dummy
184
+ print('RESULT:' + json.dumps({'success': False, 'error': 'No player controller available. Run in PIE mode first.'}))
185
+ else:
186
+ # Create the widget
187
+ widget = unreal.WidgetBlueprintLibrary.create(world, widget_class, player_controller)
188
+ if widget:
189
+ # Add to viewport
190
+ widget.add_to_viewport(z_order)
191
+ print('RESULT:' + json.dumps({'success': True}))
192
+ else:
193
+ print('RESULT:' + json.dumps({'success': False, 'error': 'Failed to create widget instance'}))
194
+ except Exception as e:
195
+ print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
196
+ `.trim();
197
+ try {
198
+ const resp = await this.bridge.executePython(py);
199
+ const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
200
+ const m = out.match(/RESULT:({.*})/);
201
+ if (m) {
202
+ try {
203
+ const parsed = JSON.parse(m[1]);
204
+ return parsed.success
205
+ ? { success: true, message: `Widget added to viewport with z-order ${zOrder}` }
206
+ : { success: false, error: parsed.error };
207
+ }
208
+ catch { }
209
+ }
210
+ return { success: true, message: 'Widget add to viewport attempted' };
211
+ }
212
+ catch (e) {
213
+ return { success: false, error: `Failed to add widget to viewport: ${e}` };
214
+ }
215
+ }
216
+ // Remove widget from viewport
217
+ async removeWidgetFromViewport(params) {
218
+ const playerIndex = params.playerIndex ?? 0;
219
+ const command = `RemoveWidgetFromViewport ${params.widgetName} ${playerIndex}`;
220
+ return this.bridge.executeConsoleCommand(command);
221
+ }
222
+ // Create menu
223
+ async createMenu(params) {
224
+ const commands = [];
225
+ commands.push(`CreateMenuWidget ${params.name} ${params.menuType}`);
226
+ if (params.buttons) {
227
+ for (const button of params.buttons) {
228
+ const pos = button.position || [0, 0];
229
+ commands.push(`AddMenuButton ${params.name} "${button.text}" ${button.action} ${pos.join(' ')}`);
230
+ }
231
+ }
232
+ for (const cmd of commands) {
233
+ await this.bridge.executeConsoleCommand(cmd);
234
+ }
235
+ return { success: true, message: `Menu ${params.name} created` };
236
+ }
237
+ // Set widget animation
238
+ async createWidgetAnimation(params) {
239
+ const commands = [];
240
+ commands.push(`CreateWidgetAnimation ${params.widgetName} ${params.animationName} ${params.duration}`);
241
+ if (params.tracks) {
242
+ for (const track of params.tracks) {
243
+ commands.push(`AddAnimationTrack ${params.widgetName}.${params.animationName} ${track.componentName} ${track.property}`);
244
+ for (const keyframe of track.keyframes) {
245
+ const value = Array.isArray(keyframe.value) ? keyframe.value.join(' ') : keyframe.value;
246
+ commands.push(`AddAnimationKeyframe ${params.widgetName}.${params.animationName} ${track.componentName} ${keyframe.time} ${value}`);
247
+ }
248
+ }
249
+ }
250
+ for (const cmd of commands) {
251
+ await this.bridge.executeConsoleCommand(cmd);
252
+ }
253
+ return { success: true, message: `Animation ${params.animationName} created` };
254
+ }
255
+ // Play widget animation
256
+ async playWidgetAnimation(params) {
257
+ const playMode = params.playMode || 'Forward';
258
+ const loops = params.loops ?? 1;
259
+ const command = `PlayWidgetAnimation ${params.widgetName} ${params.animationName} ${playMode} ${loops}`;
260
+ return this.bridge.executeConsoleCommand(command);
261
+ }
262
+ // Set widget style
263
+ async setWidgetStyle(params) {
264
+ const commands = [];
265
+ if (params.style.backgroundColor) {
266
+ commands.push(`SetWidgetBackgroundColor ${params.widgetName}.${params.componentName} ${params.style.backgroundColor.join(' ')}`);
267
+ }
268
+ if (params.style.borderColor) {
269
+ commands.push(`SetWidgetBorderColor ${params.widgetName}.${params.componentName} ${params.style.borderColor.join(' ')}`);
270
+ }
271
+ if (params.style.borderWidth !== undefined) {
272
+ commands.push(`SetWidgetBorderWidth ${params.widgetName}.${params.componentName} ${params.style.borderWidth}`);
273
+ }
274
+ if (params.style.padding) {
275
+ commands.push(`SetWidgetPadding ${params.widgetName}.${params.componentName} ${params.style.padding.join(' ')}`);
276
+ }
277
+ if (params.style.margin) {
278
+ commands.push(`SetWidgetMargin ${params.widgetName}.${params.componentName} ${params.style.margin.join(' ')}`);
279
+ }
280
+ for (const cmd of commands) {
281
+ await this.bridge.executeConsoleCommand(cmd);
282
+ }
283
+ return { success: true, message: 'Widget style updated' };
284
+ }
285
+ // Bind widget event
286
+ async bindWidgetEvent(params) {
287
+ const command = `BindWidgetEvent ${params.widgetName}.${params.componentName} ${params.eventType} ${params.functionName}`;
288
+ return this.bridge.executeConsoleCommand(command);
289
+ }
290
+ // Set input mode
291
+ async setInputMode(params) {
292
+ const commands = [];
293
+ commands.push(`SetInputMode ${params.mode}`);
294
+ if (params.showCursor !== undefined) {
295
+ commands.push(`ShowMouseCursor ${params.showCursor}`);
296
+ }
297
+ if (params.lockCursor !== undefined) {
298
+ commands.push(`SetMouseLockMode ${params.lockCursor}`);
299
+ }
300
+ for (const cmd of commands) {
301
+ await this.bridge.executeConsoleCommand(cmd);
302
+ }
303
+ return { success: true, message: `Input mode set to ${params.mode}` };
304
+ }
305
+ // Create tooltip
306
+ async createTooltip(params) {
307
+ const delay = params.delay ?? 0.5;
308
+ const command = `SetWidgetTooltip ${params.widgetName}.${params.componentName} "${params.text}" ${delay}`;
309
+ return this.bridge.executeConsoleCommand(command);
310
+ }
311
+ // Create drag and drop
312
+ async setupDragDrop(params) {
313
+ const commands = [];
314
+ commands.push(`EnableDragDrop ${params.widgetName}.${params.componentName}`);
315
+ if (params.dragVisual) {
316
+ commands.push(`SetDragVisual ${params.widgetName}.${params.componentName} ${params.dragVisual}`);
317
+ }
318
+ if (params.dropTargets) {
319
+ for (const target of params.dropTargets) {
320
+ commands.push(`AddDropTarget ${params.widgetName}.${params.componentName} ${target}`);
321
+ }
322
+ }
323
+ for (const cmd of commands) {
324
+ await this.bridge.executeConsoleCommand(cmd);
325
+ }
326
+ return { success: true, message: 'Drag and drop configured' };
327
+ }
328
+ // Create notification
329
+ async showNotification(params) {
330
+ const duration = params.duration ?? 3.0;
331
+ const type = params.type || 'Info';
332
+ const position = params.position || 'TopRight';
333
+ const command = `ShowNotification "${params.text}" ${duration} ${type} ${position}`;
334
+ return this.bridge.executeConsoleCommand(command);
335
+ }
336
+ }
337
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1,29 @@
1
+ import { UnrealBridge } from '../unreal-bridge.js';
2
+ export declare class VisualTools {
3
+ private bridge;
4
+ private env;
5
+ constructor(bridge: UnrealBridge);
6
+ takeScreenshot(params: {
7
+ resolution?: string;
8
+ }): Promise<{
9
+ success: boolean;
10
+ message: string;
11
+ imagePath?: undefined;
12
+ imageBase64?: undefined;
13
+ error?: undefined;
14
+ } | {
15
+ success: boolean;
16
+ imagePath: string;
17
+ imageBase64: string | undefined;
18
+ message?: undefined;
19
+ error?: undefined;
20
+ } | {
21
+ success: boolean;
22
+ error: string;
23
+ message?: undefined;
24
+ imagePath?: undefined;
25
+ imageBase64?: undefined;
26
+ }>;
27
+ private findLatestScreenshot;
28
+ }
29
+ //# sourceMappingURL=visual.d.ts.map
@@ -0,0 +1,67 @@
1
+ import { loadEnv } from '../types/env.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ export class VisualTools {
5
+ bridge;
6
+ env = loadEnv();
7
+ constructor(bridge) {
8
+ this.bridge = bridge;
9
+ }
10
+ // Take a screenshot of viewport (high res or standard). Returns path and base64 (truncated)
11
+ async takeScreenshot(params) {
12
+ const res = params.resolution && /^\d+x\d+$/i.test(params.resolution) ? params.resolution : '';
13
+ const cmd = res ? `HighResShot ${res}` : 'HighResShot';
14
+ try {
15
+ await this.bridge.executeConsoleCommand(cmd);
16
+ // Give the engine a moment to write the file
17
+ await new Promise(r => setTimeout(r, 1200));
18
+ const p = await this.findLatestScreenshot();
19
+ if (!p)
20
+ return { success: true, message: 'Screenshot triggered, but could not locate output file' };
21
+ let b64;
22
+ try {
23
+ const buf = fs.readFileSync(p);
24
+ // Limit to ~1MB to avoid huge responses
25
+ const max = 1024 * 1024;
26
+ b64 = buf.length > max ? buf.subarray(0, max).toString('base64') : buf.toString('base64');
27
+ }
28
+ catch { }
29
+ return { success: true, imagePath: p, imageBase64: b64 };
30
+ }
31
+ catch (err) {
32
+ return { success: false, error: String(err?.message || err) };
33
+ }
34
+ }
35
+ async findLatestScreenshot() {
36
+ // Try env override, otherwise look in common UE Saved/Screenshots folder under project
37
+ const candidates = [];
38
+ if (this.env.UE_SCREENSHOT_DIR)
39
+ candidates.push(this.env.UE_SCREENSHOT_DIR);
40
+ if (this.env.UE_PROJECT_PATH) {
41
+ const projectDir = path.dirname(this.env.UE_PROJECT_PATH);
42
+ candidates.push(path.join(projectDir, 'Saved', 'Screenshots'));
43
+ candidates.push(path.join(projectDir, 'Saved', 'Screenshots', 'Windows'));
44
+ candidates.push(path.join(projectDir, 'Saved', 'Screenshots', 'WindowsEditor'));
45
+ }
46
+ // Fallback: common locations
47
+ candidates.push(path.join(process.cwd(), 'Saved', 'Screenshots'));
48
+ candidates.push(path.join(process.cwd(), 'Saved', 'Screenshots', 'Windows'));
49
+ candidates.push(path.join(process.cwd(), 'Saved', 'Screenshots', 'WindowsEditor'));
50
+ let latest = null;
51
+ for (const c of candidates) {
52
+ try {
53
+ const entries = fs.readdirSync(c).map(f => path.join(c, f));
54
+ for (const fp of entries) {
55
+ if (!/\.png$/i.test(fp))
56
+ continue;
57
+ const st = fs.statSync(fp);
58
+ if (!latest || st.mtimeMs > latest.mtime)
59
+ latest = { path: fp, mtime: st.mtimeMs };
60
+ }
61
+ }
62
+ catch { }
63
+ }
64
+ return latest?.path || null;
65
+ }
66
+ }
67
+ //# sourceMappingURL=visual.js.map
@@ -0,0 +1,10 @@
1
+ export interface Env {
2
+ UE_HOST: string;
3
+ UE_RC_WS_PORT: number;
4
+ UE_RC_HTTP_PORT: number;
5
+ UE_PROJECT_PATH?: string;
6
+ UE_EDITOR_EXE?: string;
7
+ UE_SCREENSHOT_DIR?: string;
8
+ }
9
+ export declare function loadEnv(): Env;
10
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1,18 @@
1
+ export function loadEnv() {
2
+ const host = process.env.UE_HOST || '127.0.0.1';
3
+ // Note: UE5 default is HTTP on 30010, WebSocket on 30020
4
+ const wsPort = Number(process.env.UE_RC_WS_PORT || process.env.UE_REMOTE_CONTROL_WS_PORT || 30020);
5
+ const httpPort = Number(process.env.UE_RC_HTTP_PORT || process.env.UE_REMOTE_CONTROL_HTTP_PORT || 30010);
6
+ const projectPath = process.env.UE_PROJECT_PATH;
7
+ const editorExe = process.env.UE_EDITOR_EXE;
8
+ const screenshotDir = process.env.UE_SCREENSHOT_DIR;
9
+ return {
10
+ UE_HOST: host,
11
+ UE_RC_WS_PORT: wsPort,
12
+ UE_RC_HTTP_PORT: httpPort,
13
+ UE_PROJECT_PATH: projectPath,
14
+ UE_EDITOR_EXE: editorExe,
15
+ UE_SCREENSHOT_DIR: screenshotDir,
16
+ };
17
+ }
18
+ //# sourceMappingURL=env.js.map