unreal-engine-mcp-server 0.4.7 → 0.5.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 (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,23 +1,18 @@
1
+ import { BaseTool } from './base-tool.js';
1
2
  import { toVec3Object, toRotObject } from '../utils/normalize.js';
2
- import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
3
- export class EditorTools {
4
- bridge;
5
- constructor(bridge) {
6
- this.bridge = bridge;
7
- }
3
+ import { DEFAULT_SCREENSHOT_RESOLUTION } from '../constants.js';
4
+ import { wasmIntegration } from '../wasm/index.js';
5
+ export class EditorTools extends BaseTool {
6
+ cameraBookmarks = new Map();
7
+ editorPreferences = new Map();
8
+ activeRecording;
8
9
  async isInPIE() {
9
10
  try {
10
- const pythonCmd = `
11
- import unreal
12
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
13
- if les:
14
- print("PIE_STATE:" + str(les.is_in_play_in_editor()))
15
- else:
16
- print("PIE_STATE:False")
17
- `.trim();
18
- const resp = await this.bridge.executePython(pythonCmd);
19
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
20
- return out.includes('PIE_STATE:True');
11
+ const response = await this.sendAutomationRequest('check_pie_state', {}, { timeoutMs: 5000 });
12
+ if (response && response.success !== false) {
13
+ return response.isInPIE === true || response.result?.isInPIE === true;
14
+ }
15
+ return false;
21
16
  }
22
17
  catch {
23
18
  return false;
@@ -26,70 +21,26 @@ else:
26
21
  async ensureNotInPIE() {
27
22
  if (await this.isInPIE()) {
28
23
  await this.stopPlayInEditor();
29
- // Wait a bit for PIE to fully stop
30
24
  await new Promise(resolve => setTimeout(resolve, 500));
31
25
  }
32
26
  }
33
- async playInEditor() {
27
+ async playInEditor(timeoutMs = 30000) {
34
28
  try {
35
- // Set tick rate to match UI play (60 fps for game mode)
36
- await this.bridge.executeConsoleCommand('t.MaxFPS 60');
37
- // Try Python first using the modern LevelEditorSubsystem
38
29
  try {
39
- // Use LevelEditorSubsystem to play in the selected viewport (modern API)
40
- const pythonCmd = `
41
- import unreal, time, json
42
- # Start PIE using LevelEditorSubsystem (modern approach)
43
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
44
- if les:
45
- # Store initial state
46
- was_playing = les.is_in_play_in_editor()
47
-
48
- # Request PIE in the current viewport
49
- les.editor_play_simulate()
50
-
51
- # Wait for PIE to start with multiple checks
52
- max_attempts = 10
53
- for i in range(max_attempts):
54
- time.sleep(0.2) # Wait 200ms between checks
55
- is_playing = les.is_in_play_in_editor()
56
- if is_playing and not was_playing:
57
- # PIE has started
58
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
59
- break
60
- else:
61
- # If we've waited 2 seconds total and PIE hasn't started,
62
- # but the command was sent, assume it will start
63
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
64
- else:
65
- # If subsystem not available, report error
66
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
67
- `.trim();
68
- const resp = await this.bridge.executePython(pythonCmd);
69
- const interpreted = interpretStandardResult(resp, {
70
- successMessage: 'PIE started',
71
- failureMessage: 'Failed to start PIE'
72
- });
73
- if (interpreted.success) {
74
- const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
75
- return { success: true, message: `PIE started (via ${method})` };
30
+ const response = await this.sendAutomationRequest('control_editor', { action: 'play' }, { timeoutMs });
31
+ if (response && response.success === true) {
32
+ return { success: true, message: response.message || 'PIE started' };
76
33
  }
77
- // If not verified, fall through to fallback
34
+ return { success: false, error: response?.error || response?.message || 'Failed to start PIE' };
78
35
  }
79
36
  catch (err) {
80
- // Log the error for debugging but continue
81
- console.error('Python PIE start issue:', err);
37
+ if (err.message && /time.*out/i.test(err.message)) {
38
+ return { success: false, error: `Timeout waiting for PIE to start: ${err.message}` };
39
+ }
40
+ await this.bridge.executeConsoleCommand('t.MaxFPS 60');
41
+ await this.bridge.executeConsoleCommand('PlayInViewport');
42
+ return { success: true, message: 'PIE start command sent' };
82
43
  }
83
- // Fallback to console command which is more reliable
84
- await this.bridge.executeConsoleCommand('PlayInViewport');
85
- // Wait a moment and verify PIE started
86
- await new Promise(resolve => setTimeout(resolve, 1000));
87
- // Check if PIE is now active
88
- const isPlaying = await this.isInPIE();
89
- return {
90
- success: true,
91
- message: isPlaying ? 'PIE started successfully' : 'PIE start command sent (may take a moment)'
92
- };
93
44
  }
94
45
  catch (err) {
95
46
  return { success: false, error: `Failed to start PIE: ${err}` };
@@ -97,35 +48,20 @@ else:
97
48
  }
98
49
  async stopPlayInEditor() {
99
50
  try {
100
- // Try Python first using the modern LevelEditorSubsystem
101
51
  try {
102
- const pythonCmd = `
103
- import unreal, time, json
104
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
105
- if les:
106
- # Use correct method name for stopping PIE
107
- les.editor_request_end_play() # Modern API method
108
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
109
- else:
110
- # If subsystem not available, report error
111
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
112
- `.trim();
113
- const resp = await this.bridge.executePython(pythonCmd);
114
- const interpreted = interpretStandardResult(resp, {
115
- successMessage: 'PIE stopped successfully',
116
- failureMessage: 'Failed to stop PIE'
117
- });
118
- if (interpreted.success) {
119
- const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
120
- return { success: true, message: `PIE stopped via ${method}` };
121
- }
122
- if (interpreted.error) {
123
- return { success: false, error: interpreted.error };
52
+ const response = await this.sendAutomationRequest('control_editor', { action: 'stop' }, { timeoutMs: 30000 });
53
+ if (response.success !== false) {
54
+ return {
55
+ success: true,
56
+ message: response.message || 'PIE stopped successfully'
57
+ };
124
58
  }
125
- return { success: false, error: 'Failed to stop PIE' };
59
+ return {
60
+ success: false,
61
+ error: response.error || response.message || 'Failed to stop PIE'
62
+ };
126
63
  }
127
- catch {
128
- // Fallback to console command
64
+ catch (_pluginErr) {
129
65
  await this.bridge.executeConsoleCommand('stop');
130
66
  return { success: true, message: 'PIE stopped via console command' };
131
67
  }
@@ -136,69 +72,44 @@ else:
136
72
  }
137
73
  async pausePlayInEditor() {
138
74
  try {
139
- // Pause/Resume PIE
140
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
141
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
142
- functionName: 'ExecuteConsoleCommand',
143
- parameters: {
144
- WorldContextObject: null,
145
- Command: 'pause',
146
- SpecificPlayer: null
147
- },
148
- generateTransaction: false
149
- });
75
+ await this.bridge.executeConsoleCommand('pause');
150
76
  return { success: true, message: 'PIE paused/resumed' };
151
77
  }
152
78
  catch (err) {
153
79
  return { success: false, error: `Failed to pause PIE: ${err}` };
154
80
  }
155
81
  }
156
- // Alias for consistency with naming convention
157
82
  async pauseInEditor() {
158
83
  return this.pausePlayInEditor();
159
84
  }
160
85
  async buildLighting() {
161
86
  try {
162
- // Use modern LevelEditorSubsystem to build lighting
163
- const py = `
164
- import unreal
165
- import json
166
- try:
167
- # Use modern LevelEditorSubsystem API
168
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
169
- if les:
170
- # build_light_maps(quality, with_reflection_captures)
171
- les.build_light_maps(unreal.LightingBuildQuality.QUALITY_HIGH, True)
172
- print('RESULT:' + json.dumps({'success': True, 'message': 'Lighting build started via LevelEditorSubsystem'}))
173
- else:
174
- # If subsystem not available, report error
175
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
176
- except Exception as e:
177
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
178
- `.trim();
179
- const resp = await this.bridge.executePython(py);
180
- const interpreted = interpretStandardResult(resp, {
181
- successMessage: 'Lighting build started',
182
- failureMessage: 'Failed to build lighting'
183
- });
184
- if (interpreted.success) {
185
- return { success: true, message: interpreted.message };
186
- }
187
- return {
188
- success: false,
189
- error: interpreted.error ?? 'Failed to build lighting',
190
- details: bestEffortInterpretedText(interpreted)
191
- };
87
+ await this.bridge.executeConsoleCommand('BuildLighting');
88
+ return { success: true, message: 'Lighting build started' };
192
89
  }
193
90
  catch (err) {
194
91
  return { success: false, error: `Failed to build lighting: ${err}` };
195
92
  }
196
93
  }
94
+ async getViewportCameraInfo() {
95
+ try {
96
+ const resp = await this.sendAutomationRequest('control_editor', { action: 'get_camera' }, { timeoutMs: 3000 });
97
+ const result = resp?.result ?? resp;
98
+ const loc = result?.location ?? result?.camera?.location;
99
+ const rot = result?.rotation ?? result?.camera?.rotation;
100
+ const locArr = Array.isArray(loc) && loc.length === 3 ? [Number(loc[0]) || 0, Number(loc[1]) || 0, Number(loc[2]) || 0] : undefined;
101
+ const rotArr = Array.isArray(rot) && rot.length === 3 ? [Number(rot[0]) || 0, Number(rot[1]) || 0, Number(rot[2]) || 0] : undefined;
102
+ if (resp && resp.success !== false && locArr && rotArr) {
103
+ return { success: true, location: locArr, rotation: rotArr };
104
+ }
105
+ return { success: false, error: 'Failed to get camera information' };
106
+ }
107
+ catch (err) {
108
+ return { success: false, error: `Camera query failed: ${err}` };
109
+ }
110
+ }
197
111
  async setViewportCamera(location, rotation) {
198
- // Special handling for when both location and rotation are missing/invalid
199
- // Allow rotation-only updates
200
112
  if (location === null) {
201
- // Explicit null is not allowed for location
202
113
  throw new Error('Invalid location: null is not allowed');
203
114
  }
204
115
  if (location !== undefined && location !== null) {
@@ -206,14 +117,12 @@ except Exception as e:
206
117
  if (!locObj) {
207
118
  throw new Error('Invalid location: must be {x,y,z} or [x,y,z]');
208
119
  }
209
- // Clamp extreme values to reasonable limits for Unreal Engine
210
- const MAX_COORD = 1000000; // 1 million units is a reasonable max for UE
120
+ const MAX_COORD = 1000000;
211
121
  locObj.x = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.x));
212
122
  locObj.y = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.y));
213
123
  locObj.z = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.z));
214
124
  location = locObj;
215
125
  }
216
- // Validate rotation if provided
217
126
  if (rotation !== undefined) {
218
127
  if (rotation === null) {
219
128
  throw new Error('Invalid rotation: null is not allowed');
@@ -222,107 +131,36 @@ except Exception as e:
222
131
  if (!rotObj) {
223
132
  throw new Error('Invalid rotation: must be {pitch,yaw,roll} or [pitch,yaw,roll]');
224
133
  }
225
- // Normalize rotation values to 0-360 range
226
134
  rotObj.pitch = ((rotObj.pitch % 360) + 360) % 360;
227
135
  rotObj.yaw = ((rotObj.yaw % 360) + 360) % 360;
228
136
  rotObj.roll = ((rotObj.roll % 360) + 360) % 360;
229
137
  rotation = rotObj;
230
138
  }
231
139
  try {
232
- // Try Python for actual viewport camera positioning
233
- // Only proceed if we have a valid location
234
- if (location) {
235
- try {
236
- const rot = rotation || { pitch: 0, yaw: 0, roll: 0 };
237
- const pythonCmd = `
238
- import unreal
239
- # Use UnrealEditorSubsystem instead of deprecated EditorLevelLibrary
240
- ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
241
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
242
- location = unreal.Vector(${location.x}, ${location.y}, ${location.z})
243
- rotation = unreal.Rotator(${rot.pitch}, ${rot.yaw}, ${rot.roll})
244
- if ues:
245
- ues.set_level_viewport_camera_info(location, rotation)
246
- # Invalidate viewports to ensure visual update
247
- try:
248
- if les:
249
- les.editor_invalidate_viewports()
250
- except Exception:
251
- pass
252
- `.trim();
253
- await this.bridge.executePython(pythonCmd);
254
- return {
255
- success: true,
256
- message: 'Viewport camera positioned via UnrealEditorSubsystem'
257
- };
258
- }
259
- catch {
260
- // Fallback to camera speed control
261
- await this.bridge.executeConsoleCommand('camspeed 4');
262
- return {
263
- success: true,
264
- message: 'Camera speed set. Use debug camera (toggledebugcamera) for manual positioning'
265
- };
266
- }
267
- }
268
- else if (rotation) {
269
- // Only rotation provided, try to set just rotation
270
- try {
271
- const pythonCmd = `
272
- import unreal
273
- # Use UnrealEditorSubsystem to read/write viewport camera
274
- ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
275
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
276
- rotation = unreal.Rotator(${rotation.pitch}, ${rotation.yaw}, ${rotation.roll})
277
- if ues:
278
- info = ues.get_level_viewport_camera_info()
279
- if info is not None:
280
- current_location, _ = info
281
- ues.set_level_viewport_camera_info(current_location, rotation)
282
- try:
283
- if les:
284
- les.editor_invalidate_viewports()
285
- except Exception:
286
- pass
287
- `.trim();
288
- await this.bridge.executePython(pythonCmd);
289
- return {
290
- success: true,
291
- message: 'Viewport camera rotation set via UnrealEditorSubsystem'
292
- };
293
- }
294
- catch {
295
- // Fallback
296
- return {
297
- success: true,
298
- message: 'Camera rotation update attempted'
299
- };
300
- }
301
- }
302
- else {
303
- // Neither location nor rotation provided - this is valid, just no-op
304
- return {
305
- success: true,
306
- message: 'No camera changes requested'
307
- };
140
+ const locArray = location
141
+ ? [(location.x ?? location[0] ?? 0), (location.y ?? location[1] ?? 0), (location.z ?? location[2] ?? 0)]
142
+ : [0, 0, 0];
143
+ const rotArray = rotation
144
+ ? [(rotation.pitch ?? rotation[0] ?? 0), (rotation.yaw ?? rotation[1] ?? 0), (rotation.roll ?? rotation[2] ?? 0)]
145
+ : [0, 0, 0];
146
+ wasmIntegration.composeTransform(locArray, rotArray, [1, 1, 1]);
147
+ const resp = await this.sendAutomationRequest('control_editor', {
148
+ action: 'set_camera',
149
+ location: location,
150
+ rotation: rotation
151
+ }, { timeoutMs: 10000 });
152
+ if (resp && resp.success === true) {
153
+ return { success: true, message: resp.message || 'Camera set', location, rotation };
308
154
  }
155
+ return { success: false, error: resp?.error || resp?.message || 'Failed to set camera' };
309
156
  }
310
157
  catch (err) {
311
- return { success: false, error: `Failed to set camera: ${err}` };
158
+ return { success: false, error: `Camera control failed: ${err}` };
312
159
  }
313
160
  }
314
161
  async setCameraSpeed(speed) {
315
162
  try {
316
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
317
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
318
- functionName: 'ExecuteConsoleCommand',
319
- parameters: {
320
- WorldContextObject: null,
321
- Command: `camspeed ${speed}`,
322
- SpecificPlayer: null
323
- },
324
- generateTransaction: false
325
- });
163
+ await this.bridge.executeConsoleCommand(`camspeed ${speed}`);
326
164
  return { success: true, message: `Camera speed set to ${speed}` };
327
165
  }
328
166
  catch (err) {
@@ -331,21 +169,186 @@ if ues:
331
169
  }
332
170
  async setFOV(fov) {
333
171
  try {
334
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
335
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
336
- functionName: 'ExecuteConsoleCommand',
337
- parameters: {
338
- WorldContextObject: null,
339
- Command: `fov ${fov}`,
340
- SpecificPlayer: null
341
- },
342
- generateTransaction: false
343
- });
172
+ await this.bridge.executeConsoleCommand(`fov ${fov}`);
344
173
  return { success: true, message: `FOV set to ${fov}` };
345
174
  }
346
175
  catch (err) {
347
176
  return { success: false, error: `Failed to set FOV: ${err}` };
348
177
  }
349
178
  }
179
+ async takeScreenshot(filename, resolution) {
180
+ try {
181
+ if (resolution && !/^\d+x\d+$/.test(resolution)) {
182
+ return { success: false, error: 'Invalid resolution format. Use WxH (e.g. 1920x1080)' };
183
+ }
184
+ const sanitizedFilename = filename ? filename.replace(/[<>:*?"|]/g, '_') : `Screenshot_${Date.now()}`;
185
+ const resString = resolution || DEFAULT_SCREENSHOT_RESOLUTION;
186
+ const command = filename ? `highresshot ${resString} filename="${sanitizedFilename}"` : 'shot';
187
+ await this.bridge.executeConsoleCommand(command);
188
+ return {
189
+ success: true,
190
+ message: `Screenshot captured: ${sanitizedFilename}`,
191
+ filename: sanitizedFilename,
192
+ command
193
+ };
194
+ }
195
+ catch (err) {
196
+ return { success: false, error: `Failed to take screenshot: ${err}` };
197
+ }
198
+ }
199
+ async resumePlayInEditor() {
200
+ try {
201
+ await this.bridge.executeConsoleCommand('pause');
202
+ return {
203
+ success: true,
204
+ message: 'PIE resume toggled via pause command'
205
+ };
206
+ }
207
+ catch (err) {
208
+ return { success: false, error: `Failed to resume PIE: ${err}` };
209
+ }
210
+ }
211
+ async stepPIEFrame(steps = 1) {
212
+ const clampedSteps = Number.isFinite(steps) ? Math.max(1, Math.floor(steps)) : 1;
213
+ try {
214
+ for (let index = 0; index < clampedSteps; index += 1) {
215
+ await this.bridge.executeConsoleCommand('Step=1');
216
+ }
217
+ return {
218
+ success: true,
219
+ message: `Advanced PIE by ${clampedSteps} frame(s)`,
220
+ steps: clampedSteps
221
+ };
222
+ }
223
+ catch (err) {
224
+ return { success: false, error: `Failed to step PIE: ${err}` };
225
+ }
226
+ }
227
+ async startRecording(options) {
228
+ const startedAt = Date.now();
229
+ this.activeRecording = {
230
+ name: typeof options?.filename === 'string' ? options.filename.trim() : undefined,
231
+ options: options ? { ...options } : undefined,
232
+ startedAt
233
+ };
234
+ return {
235
+ success: true,
236
+ message: 'Recording session started',
237
+ recording: {
238
+ name: this.activeRecording.name,
239
+ startedAt,
240
+ options: this.activeRecording.options
241
+ }
242
+ };
243
+ }
244
+ async stopRecording() {
245
+ if (!this.activeRecording) {
246
+ return {
247
+ success: true,
248
+ message: 'No active recording session to stop'
249
+ };
250
+ }
251
+ const stoppedRecording = this.activeRecording;
252
+ this.activeRecording = undefined;
253
+ return {
254
+ success: true,
255
+ message: 'Recording session stopped',
256
+ recording: stoppedRecording
257
+ };
258
+ }
259
+ async createCameraBookmark(name) {
260
+ const trimmedName = name.trim();
261
+ if (!trimmedName) {
262
+ return { success: false, error: 'bookmarkName is required' };
263
+ }
264
+ const cameraInfo = await this.getViewportCameraInfo();
265
+ if (!cameraInfo.success || !cameraInfo.location || !cameraInfo.rotation) {
266
+ return {
267
+ success: false,
268
+ error: cameraInfo.error || 'Failed to capture viewport camera'
269
+ };
270
+ }
271
+ this.cameraBookmarks.set(trimmedName, {
272
+ location: cameraInfo.location,
273
+ rotation: cameraInfo.rotation,
274
+ savedAt: Date.now()
275
+ });
276
+ return {
277
+ success: true,
278
+ message: `Bookmark '${trimmedName}' saved`,
279
+ bookmark: {
280
+ name: trimmedName,
281
+ location: cameraInfo.location,
282
+ rotation: cameraInfo.rotation
283
+ }
284
+ };
285
+ }
286
+ async jumpToCameraBookmark(name) {
287
+ const trimmedName = name.trim();
288
+ if (!trimmedName) {
289
+ return { success: false, error: 'bookmarkName is required' };
290
+ }
291
+ const bookmark = this.cameraBookmarks.get(trimmedName);
292
+ if (!bookmark) {
293
+ return {
294
+ success: false,
295
+ error: `Bookmark '${trimmedName}' not found`
296
+ };
297
+ }
298
+ await this.setViewportCamera({ x: bookmark.location[0], y: bookmark.location[1], z: bookmark.location[2] }, { pitch: bookmark.rotation[0], yaw: bookmark.rotation[1], roll: bookmark.rotation[2] });
299
+ return {
300
+ success: true,
301
+ message: `Jumped to bookmark '${trimmedName}'`
302
+ };
303
+ }
304
+ async setEditorPreferences(category, preferences) {
305
+ const resolvedCategory = typeof category === 'string' && category.trim().length > 0 ? category.trim() : 'General';
306
+ const existing = this.editorPreferences.get(resolvedCategory) ?? {};
307
+ this.editorPreferences.set(resolvedCategory, { ...existing, ...preferences });
308
+ return {
309
+ success: true,
310
+ message: `Preferences stored for ${resolvedCategory}`,
311
+ preferences: this.editorPreferences.get(resolvedCategory)
312
+ };
313
+ }
314
+ async setViewportResolution(width, height) {
315
+ try {
316
+ const clampedWidth = Math.max(320, Math.min(7680, width));
317
+ const clampedHeight = Math.max(240, Math.min(4320, height));
318
+ const command = `r.SetRes ${clampedWidth}x${clampedHeight}`;
319
+ await this.bridge.executeConsoleCommand(command);
320
+ return {
321
+ success: true,
322
+ message: `Viewport resolution set to ${clampedWidth}x${clampedHeight}`,
323
+ width: clampedWidth,
324
+ height: clampedHeight
325
+ };
326
+ }
327
+ catch (err) {
328
+ return { success: false, error: `Failed to set viewport resolution: ${err}` };
329
+ }
330
+ }
331
+ async executeConsoleCommand(command) {
332
+ try {
333
+ if (!command || typeof command !== 'string') {
334
+ return { success: false, error: 'Invalid command: must be a non-empty string' };
335
+ }
336
+ if (command.length > 1000) {
337
+ return {
338
+ success: false,
339
+ error: `Command too long (${command.length} chars). Maximum is 1000 characters.`
340
+ };
341
+ }
342
+ const res = await this.bridge.executeConsoleCommand(command);
343
+ return {
344
+ success: true,
345
+ message: `Console command executed: ${command}`,
346
+ output: res
347
+ };
348
+ }
349
+ catch (err) {
350
+ return { success: false, error: `Failed to execute console command: ${err}` };
351
+ }
352
+ }
350
353
  }
351
354
  //# sourceMappingURL=editor.js.map
@@ -1,8 +1,10 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
+ import { AutomationBridge } from '../automation/index.js';
2
3
  export declare class EngineTools {
3
- private bridge;
4
+ private automationBridge?;
4
5
  private env;
5
- constructor(bridge: UnrealBridge);
6
+ constructor(_bridge: UnrealBridge, automationBridge?: AutomationBridge | undefined);
7
+ setAutomationBridge(automationBridge?: AutomationBridge): void;
6
8
  launchEditor(params?: {
7
9
  editorExe?: string;
8
10
  projectPath?: string;
@@ -19,12 +21,16 @@ export declare class EngineTools {
19
21
  }>;
20
22
  quitEditor(): Promise<{
21
23
  success: boolean;
24
+ error: string;
22
25
  message: string;
23
- error?: undefined;
24
26
  } | {
25
27
  success: boolean;
26
- error: string;
28
+ error: any;
27
29
  message?: undefined;
30
+ } | {
31
+ success: boolean;
32
+ message: string;
33
+ error?: undefined;
28
34
  }>;
29
35
  }
30
36
  //# sourceMappingURL=engine.d.ts.map
@@ -1,10 +1,13 @@
1
1
  import { loadEnv } from '../types/env.js';
2
2
  import { spawn } from 'child_process';
3
3
  export class EngineTools {
4
- bridge;
4
+ automationBridge;
5
5
  env = loadEnv();
6
- constructor(bridge) {
7
- this.bridge = bridge;
6
+ constructor(_bridge, automationBridge) {
7
+ this.automationBridge = automationBridge;
8
+ }
9
+ setAutomationBridge(automationBridge) {
10
+ this.automationBridge = automationBridge;
8
11
  }
9
12
  async launchEditor(params) {
10
13
  const exe = params?.editorExe || this.env.UE_EDITOR_EXE;
@@ -23,9 +26,14 @@ export class EngineTools {
23
26
  }
24
27
  }
25
28
  async quitEditor() {
29
+ if (!this.automationBridge) {
30
+ return { success: false, error: 'AUTOMATION_BRIDGE_UNAVAILABLE', message: 'Automation bridge is not available for quit_editor' };
31
+ }
26
32
  try {
27
- // Use Python SystemLibrary.quit_editor if available
28
- await this.bridge.executePython('import unreal; unreal.SystemLibrary.quit_editor()');
33
+ const resp = await this.automationBridge.sendAutomationRequest('quit_editor', {});
34
+ if (resp && resp.success === false) {
35
+ return { success: false, error: resp.error || resp.message || 'Quit request failed' };
36
+ }
29
37
  return { success: true, message: 'Quit command sent' };
30
38
  }
31
39
  catch (err) {