unreal-engine-mcp-server 0.4.7 → 0.5.0

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 (438) 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.yml +148 -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 +23 -0
  19. package/.github/workflows/labeler.yml +16 -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 +12 -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 +267 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -71
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -619
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  97. package/dist/tools/consolidated-tool-definitions.js +829 -496
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1026
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +3 -3
  161. package/dist/tools/logs.js +5 -57
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +183 -19
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -663
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -515
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1139
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +9 -57
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +243 -21
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -574
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,9 +1,18 @@
1
+ import { cleanObject } from '../utils/safe-json.js';
1
2
  import { validateAssetParams } from '../utils/validation.js';
2
- import { interpretStandardResult, coerceBoolean, coerceString, coerceStringArray } from '../utils/result-helpers.js';
3
3
  export class AnimationTools {
4
4
  bridge;
5
- constructor(bridge) {
5
+ managedArtifacts = new Map();
6
+ automationBridge;
7
+ constructor(bridge, automationBridge) {
6
8
  this.bridge = bridge;
9
+ this.automationBridge = automationBridge;
10
+ }
11
+ setAutomationBridge(automationBridge) {
12
+ this.automationBridge = automationBridge;
13
+ }
14
+ trackArtifact(key, info) {
15
+ this.managedArtifacts.set(key, { ...info, createdAt: Date.now() });
7
16
  }
8
17
  async createAnimationBlueprint(params) {
9
18
  try {
@@ -16,13 +25,63 @@ export class AnimationTools {
16
25
  const sanitized = validation.sanitized;
17
26
  const assetName = sanitized.name;
18
27
  const assetPath = sanitized.savePath ?? targetPath;
19
- const script = this.buildCreateAnimationBlueprintScript({
20
- name: assetName,
21
- path: assetPath,
22
- skeletonPath: params.skeletonPath
23
- });
24
- const response = await this.bridge.executePython(script);
25
- return this.parseAnimationBlueprintResponse(response, assetName, assetPath);
28
+ const fullPath = `${assetPath}/${assetName}`;
29
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
30
+ try {
31
+ const resp = await this.automationBridge.sendAutomationRequest('create_animation_blueprint', {
32
+ name: assetName,
33
+ skeletonPath: params.skeletonPath,
34
+ savePath: assetPath
35
+ }, { timeoutMs: 60000 });
36
+ const result = resp?.result ?? resp;
37
+ const resultObj = result && typeof result === 'object' ? result : undefined;
38
+ const warnings = Array.isArray(resultObj?.warnings) ? resultObj.warnings : undefined;
39
+ const details = Array.isArray(resultObj?.details) ? resultObj.details : undefined;
40
+ const isSuccess = resp && resp.success !== false && !!resultObj;
41
+ if (isSuccess && resultObj) {
42
+ const blueprintPath = typeof resultObj.blueprintPath === 'string' ? resultObj.blueprintPath : fullPath;
43
+ this.trackArtifact(assetName, { path: blueprintPath, type: 'AnimationBlueprint' });
44
+ return {
45
+ success: true,
46
+ message: resp.message || `Animation Blueprint created at ${blueprintPath}`,
47
+ path: blueprintPath,
48
+ skeleton: params.skeletonPath,
49
+ warnings,
50
+ details
51
+ };
52
+ }
53
+ const message = typeof resp?.message === 'string'
54
+ ? resp.message
55
+ : (typeof resp?.error === 'string' ? resp.error : 'Animation Blueprint creation failed');
56
+ const error = typeof resp?.error === 'string' ? resp.error : message;
57
+ return {
58
+ success: false,
59
+ message,
60
+ error,
61
+ path: fullPath,
62
+ skeleton: params.skeletonPath,
63
+ warnings,
64
+ details
65
+ };
66
+ }
67
+ catch (err) {
68
+ const error = String(err);
69
+ return {
70
+ success: false,
71
+ message: `Failed to create Animation Blueprint: ${error}`,
72
+ error,
73
+ path: fullPath,
74
+ skeleton: params.skeletonPath
75
+ };
76
+ }
77
+ }
78
+ return {
79
+ success: false,
80
+ message: 'Automation bridge not connected for Animation Blueprint creation',
81
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE',
82
+ path: fullPath,
83
+ skeleton: params.skeletonPath
84
+ };
26
85
  }
27
86
  catch (err) {
28
87
  const error = `Failed to create Animation Blueprint: ${err}`;
@@ -65,6 +124,72 @@ export class AnimationTools {
65
124
  return { success: false, error: `Failed to add state machine: ${err}` };
66
125
  }
67
126
  }
127
+ async createStateMachine(params) {
128
+ try {
129
+ const rawName = typeof params.machineName === 'string' ? params.machineName.trim() : '';
130
+ const machineName = rawName || 'StateMachine';
131
+ const normalizedStates = Array.isArray(params.states)
132
+ ? params.states
133
+ .map((s) => {
134
+ if (typeof s === 'string') {
135
+ const name = s.trim();
136
+ return name ? { name } : undefined;
137
+ }
138
+ if (s && typeof s === 'object' && typeof s.name === 'string') {
139
+ const name = s.name.trim();
140
+ if (!name)
141
+ return undefined;
142
+ return s;
143
+ }
144
+ return undefined;
145
+ })
146
+ .filter((s) => !!s)
147
+ : [];
148
+ const normalizedTransitionsRaw = Array.isArray(params.transitions)
149
+ ? params.transitions
150
+ .map((t) => {
151
+ if (!t || typeof t !== 'object')
152
+ return undefined;
153
+ const src = (t.sourceState || '').trim();
154
+ const dst = (t.targetState || '').trim();
155
+ if (!src || !dst)
156
+ return undefined;
157
+ return { sourceState: src, targetState: dst, condition: t.condition };
158
+ })
159
+ .filter((t) => !!t)
160
+ : [];
161
+ const normalizedTransitions = normalizedTransitionsRaw;
162
+ const blueprintPath = typeof params.blueprintPath === 'string' && params.blueprintPath.trim().length > 0
163
+ ? params.blueprintPath.trim()
164
+ : undefined;
165
+ const key = `StateMachine:${machineName}`;
166
+ this.trackArtifact(key, {
167
+ path: blueprintPath,
168
+ type: 'AnimationStateMachine',
169
+ metadata: {
170
+ machineName,
171
+ states: normalizedStates,
172
+ transitions: normalizedTransitions
173
+ }
174
+ });
175
+ return {
176
+ success: true,
177
+ message: `State machine '${machineName}' specification recorded`,
178
+ machineName,
179
+ blueprintPath,
180
+ states: normalizedStates.length ? normalizedStates : undefined,
181
+ transitions: normalizedTransitions.length ? normalizedTransitions : undefined
182
+ };
183
+ }
184
+ catch (err) {
185
+ const error = String(err);
186
+ return {
187
+ success: false,
188
+ message: `Failed to record state machine specification: ${error}`,
189
+ error
190
+ };
191
+ }
192
+ }
68
193
  async createBlendSpace(params) {
69
194
  try {
70
195
  const targetPath = params.savePath ?? '/Game/Animations';
@@ -76,69 +201,401 @@ export class AnimationTools {
76
201
  const assetName = sanitized.name;
77
202
  const assetPath = sanitized.savePath ?? targetPath;
78
203
  const dimensions = params.dimensions === 2 ? 2 : 1;
79
- const blendSpaceType = dimensions === 2 ? 'BlendSpace' : 'BlendSpace1D';
80
- const commands = [
81
- `CreateAsset ${blendSpaceType} ${assetName} ${assetPath}`,
82
- `echo Creating ${blendSpaceType} ${assetName} at ${assetPath}`
83
- ];
84
- if (params.skeletonPath) {
85
- commands.push(`SetBlendSpaceSkeleton ${assetName} ${params.skeletonPath}`);
204
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
205
+ try {
206
+ const payload = {
207
+ action: 'create_blend_space',
208
+ name: assetName,
209
+ savePath: assetPath,
210
+ skeletonPath: params.skeletonPath,
211
+ dimensions,
212
+ samples: params.samples
213
+ };
214
+ if (params.horizontalAxis) {
215
+ payload.minX = params.horizontalAxis.minValue;
216
+ payload.maxX = params.horizontalAxis.maxValue;
217
+ }
218
+ if (params.verticalAxis) {
219
+ payload.minY = params.verticalAxis.minValue;
220
+ payload.maxY = params.verticalAxis.maxValue;
221
+ }
222
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload));
223
+ const result = resp?.result ?? resp;
224
+ const resultObj = result && typeof result === 'object' ? result : undefined;
225
+ const isSuccess = resp && resp.success !== false && !!resultObj;
226
+ if (isSuccess && resultObj) {
227
+ const path = typeof resultObj.blendSpacePath === 'string'
228
+ ? resultObj.blendSpacePath
229
+ : `${assetPath}/${assetName}`;
230
+ const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
231
+ const details = resultObj ? resultObj.details : undefined;
232
+ return {
233
+ success: true,
234
+ message: resp.message || `Blend Space ${assetName} created`,
235
+ path,
236
+ skeletonPath: params.skeletonPath,
237
+ details,
238
+ warnings
239
+ };
240
+ }
241
+ const message = typeof resp?.message === 'string'
242
+ ? resp.message
243
+ : (typeof resp?.error === 'string' ? resp.error : 'Blend space creation failed');
244
+ const error = typeof resp?.error === 'string' ? resp.error : message;
245
+ return { success: false, error };
246
+ }
247
+ catch (err) {
248
+ const error = String(err);
249
+ return { success: false, error: `Failed to create blend space: ${error}` };
250
+ }
251
+ }
252
+ return { success: false, error: 'Automation bridge not connected for createBlendSpace' };
253
+ }
254
+ catch (err) {
255
+ return { success: false, error: `Failed to create blend space: ${err}` };
256
+ }
257
+ }
258
+ async setupControlRig(params) {
259
+ try {
260
+ const targetPath = params.savePath ?? '/Game/Animations';
261
+ const validation = validateAssetParams({ name: params.name, savePath: targetPath });
262
+ if (!validation.valid) {
263
+ return { success: false, error: validation.error ?? 'Invalid asset parameters' };
264
+ }
265
+ const sanitized = validation.sanitized;
266
+ const assetName = sanitized.name;
267
+ const assetPath = sanitized.savePath ?? targetPath;
268
+ const fullPath = `${assetPath}/${assetName}`;
269
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
270
+ try {
271
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
272
+ action: 'setup_ik',
273
+ name: assetName,
274
+ savePath: assetPath,
275
+ skeletonPath: params.skeletonPath,
276
+ controls: params.controls
277
+ }), { timeoutMs: 60000 });
278
+ const result = resp?.result ?? resp;
279
+ const resultObj = result && typeof result === 'object' ? result : undefined;
280
+ const isSuccess = resp && resp.success !== false && !!resultObj;
281
+ if (isSuccess && resultObj) {
282
+ const controlRigPath = typeof resultObj.controlRigPath === 'string'
283
+ ? resultObj.controlRigPath
284
+ : fullPath;
285
+ const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
286
+ const details = resultObj ? resultObj.details : undefined;
287
+ this.trackArtifact(assetName, { path: controlRigPath, type: 'ControlRig' });
288
+ return {
289
+ success: true,
290
+ message: resp.message || `Control Rig ${assetName} created`,
291
+ path: controlRigPath,
292
+ warnings,
293
+ details
294
+ };
295
+ }
296
+ const message = typeof resp?.message === 'string'
297
+ ? resp.message
298
+ : (typeof resp?.error === 'string' ? resp.error : 'Control Rig setup failed');
299
+ const error = typeof resp?.error === 'string' ? resp.error : message;
300
+ return { success: false, error };
301
+ }
302
+ catch (err) {
303
+ const error = String(err);
304
+ return { success: false, error: `Failed to setup control rig: ${error}` };
305
+ }
306
+ }
307
+ return { success: false, error: 'Automation bridge not connected for setupControlRig' };
308
+ }
309
+ catch (err) {
310
+ return { success: false, error: `Failed to setup control rig: ${err}` };
311
+ }
312
+ }
313
+ async setupIK(params) {
314
+ try {
315
+ const actorName = (params.actorName || 'Character').trim();
316
+ const ikBones = Array.isArray(params.ikBones)
317
+ ? params.ikBones.map((b) => String(b)).filter((b) => b.trim().length > 0)
318
+ : [];
319
+ const key = `IK:${actorName}`;
320
+ this.trackArtifact(key, {
321
+ type: 'IKSetup',
322
+ metadata: {
323
+ actorName,
324
+ ikBones,
325
+ enableFootPlacement: params.enableFootPlacement === true
326
+ }
327
+ });
328
+ return {
329
+ success: true,
330
+ message: `IK setup specification recorded for actor '${actorName}'`,
331
+ actorName,
332
+ ikBones: ikBones.length ? ikBones : undefined,
333
+ enableFootPlacement: params.enableFootPlacement === true ? true : undefined
334
+ };
335
+ }
336
+ catch (err) {
337
+ const error = String(err);
338
+ return {
339
+ success: false,
340
+ message: `Failed to record IK setup: ${error}`,
341
+ error
342
+ };
343
+ }
344
+ }
345
+ async createProceduralAnim(params) {
346
+ try {
347
+ const baseName = (params.systemName || '').trim()
348
+ || (params.baseAnimation ? params.baseAnimation.split('/').pop() || '' : '');
349
+ const systemName = baseName || 'ProceduralSystem';
350
+ const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
351
+ const path = `${basePath || '/Game/Animations'}/${systemName}`;
352
+ this.trackArtifact(systemName, {
353
+ path,
354
+ type: 'ProceduralAnimation',
355
+ metadata: {
356
+ baseAnimation: params.baseAnimation,
357
+ modifiers: Array.isArray(params.modifiers) ? params.modifiers : []
358
+ }
359
+ });
360
+ return {
361
+ success: true,
362
+ message: `Procedural animation system '${systemName}' specification recorded at ${path}`,
363
+ path,
364
+ systemName
365
+ };
366
+ }
367
+ catch (err) {
368
+ const error = String(err);
369
+ return {
370
+ success: false,
371
+ message: `Failed to record procedural animation system: ${error}`,
372
+ error
373
+ };
374
+ }
375
+ }
376
+ async createBlendTree(params) {
377
+ try {
378
+ const rawName = (params.treeName || '').trim();
379
+ const treeName = rawName || 'BlendTree';
380
+ const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
381
+ const path = `${basePath || '/Game/Animations'}/${treeName}`;
382
+ this.trackArtifact(treeName, {
383
+ path,
384
+ type: 'BlendTree',
385
+ metadata: {
386
+ blendType: params.blendType,
387
+ basePose: params.basePose,
388
+ additiveAnimations: Array.isArray(params.additiveAnimations) ? params.additiveAnimations : []
389
+ }
390
+ });
391
+ return {
392
+ success: true,
393
+ message: `Blend tree '${treeName}' specification recorded at ${path}`,
394
+ path,
395
+ treeName
396
+ };
397
+ }
398
+ catch (err) {
399
+ const error = String(err);
400
+ return {
401
+ success: false,
402
+ message: `Failed to record blend tree specification: ${error}`,
403
+ error
404
+ };
405
+ }
406
+ }
407
+ async cleanup(artifacts) {
408
+ try {
409
+ const pathsToDelete = [];
410
+ if (Array.isArray(artifacts) && artifacts.length > 0) {
411
+ pathsToDelete.push(...artifacts.map((a) => String(a).trim()).filter((a) => a.length > 0));
412
+ }
413
+ else {
414
+ for (const [key, val] of this.managedArtifacts.entries()) {
415
+ if (val.path)
416
+ pathsToDelete.push(val.path);
417
+ else
418
+ pathsToDelete.push(key);
419
+ }
420
+ }
421
+ if (pathsToDelete.length === 0) {
422
+ return {
423
+ success: true,
424
+ message: 'No artifacts to cleanup.'
425
+ };
426
+ }
427
+ let bridgeMessage = '';
428
+ if (this.automationBridge) {
429
+ try {
430
+ const response = await this.automationBridge.sendAutomationRequest('animation_physics', {
431
+ action: 'cleanup',
432
+ artifacts: pathsToDelete
433
+ });
434
+ if (!response.success) {
435
+ bridgeMessage = ` (Engine cleanup failed: ${response.message})`;
436
+ }
437
+ else {
438
+ bridgeMessage = ' (Engine assets deleted)';
439
+ }
440
+ }
441
+ catch (e) {
442
+ bridgeMessage = ` (Engine connection failed: ${e})`;
443
+ }
444
+ }
445
+ else {
446
+ bridgeMessage = ' (No automation bridge available)';
86
447
  }
87
- if (params.horizontalAxis) {
88
- commands.push(`SetBlendSpaceAxis ${assetName} Horizontal ${params.horizontalAxis.name} ${params.horizontalAxis.minValue} ${params.horizontalAxis.maxValue}`);
448
+ const removed = [];
449
+ const toRemoveKeys = [];
450
+ for (const [key, val] of this.managedArtifacts.entries()) {
451
+ if (pathsToDelete.includes(key) || (val.path && pathsToDelete.includes(val.path))) {
452
+ toRemoveKeys.push(key);
453
+ }
89
454
  }
90
- if (dimensions === 2 && params.verticalAxis) {
91
- commands.push(`SetBlendSpaceAxis ${assetName} Vertical ${params.verticalAxis.name} ${params.verticalAxis.minValue} ${params.verticalAxis.maxValue}`);
455
+ for (const key of toRemoveKeys) {
456
+ this.managedArtifacts.delete(key);
457
+ removed.push(key);
92
458
  }
93
- if (params.samples) {
94
- for (const sample of params.samples) {
95
- const coords = dimensions === 1 ? `${sample.x}` : `${sample.x} ${sample.y ?? 0}`;
96
- commands.push(`AddBlendSpaceSample ${assetName} ${sample.animation} ${coords}`);
459
+ for (const path of pathsToDelete) {
460
+ if (!removed.includes(path)) {
461
+ removed.push(path);
97
462
  }
98
463
  }
99
- await this.bridge.executeConsoleCommands(commands);
100
464
  return {
101
465
  success: true,
102
- message: `Blend Space ${assetName} created`,
103
- path: `${assetPath}/${assetName}`
466
+ message: `Cleanup attempt processed for ${pathsToDelete.length} artifacts${bridgeMessage}`,
467
+ removed
104
468
  };
105
469
  }
106
470
  catch (err) {
107
- return { success: false, error: `Failed to create blend space: ${err}` };
471
+ const error = String(err);
472
+ return {
473
+ success: false,
474
+ message: `Failed to cleanup animation artifacts: ${error}`,
475
+ error
476
+ };
108
477
  }
109
478
  }
110
- async setupControlRig(params) {
479
+ async createAnimationAsset(params) {
111
480
  try {
112
- const targetPath = params.savePath ?? '/Game/Animations';
481
+ const targetPath = (params.path || params.savePath) ?? '/Game/Animations';
113
482
  const validation = validateAssetParams({ name: params.name, savePath: targetPath });
114
483
  if (!validation.valid) {
115
- return { success: false, error: validation.error ?? 'Invalid asset parameters' };
484
+ const message = validation.error ?? 'Invalid asset parameters';
485
+ return { success: false, message, error: message };
116
486
  }
117
487
  const sanitized = validation.sanitized;
118
488
  const assetName = sanitized.name;
119
489
  const assetPath = sanitized.savePath ?? targetPath;
120
490
  const fullPath = `${assetPath}/${assetName}`;
121
- const commands = [
122
- `CreateAsset ControlRig ${assetName} ${assetPath}`,
123
- `SetControlRigSkeleton ${assetName} ${params.skeletonPath}`
124
- ];
125
- if (params.controls) {
126
- for (const control of params.controls) {
127
- commands.push(`AddControlRigControl ${assetName} ${control.name} ${control.type} ${control.bone ?? ''}`);
128
- if (control.defaultValue !== undefined) {
129
- commands.push(`SetControlRigDefault ${assetName} ${control.name} ${JSON.stringify(control.defaultValue)}`);
491
+ const normalizedType = (params.assetType || 'sequence').toLowerCase();
492
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
493
+ try {
494
+ const payload = {
495
+ action: 'create_animation_asset',
496
+ name: assetName,
497
+ savePath: assetPath,
498
+ skeletonPath: params.skeletonPath,
499
+ assetType: normalizedType
500
+ };
501
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload), { timeoutMs: 60000 });
502
+ const result = resp?.result ?? resp;
503
+ const resultObj = result && typeof result === 'object' ? result : undefined;
504
+ const isSuccess = resp && resp.success !== false && !!resultObj;
505
+ if (isSuccess && resultObj) {
506
+ const assetPathResult = typeof resultObj.assetPath === 'string' ? resultObj.assetPath : fullPath;
507
+ const assetTypeResult = typeof resultObj.assetType === 'string' ? resultObj.assetType : undefined;
508
+ const existingAsset = typeof resultObj.existingAsset === 'boolean' ? Boolean(resultObj.existingAsset) : false;
509
+ this.trackArtifact(assetName, { path: assetPathResult, type: 'AnimationAsset' });
510
+ return {
511
+ success: true,
512
+ message: resp.message || `Animation asset created at ${assetPathResult}`,
513
+ path: assetPathResult,
514
+ assetType: assetTypeResult,
515
+ existingAsset
516
+ };
130
517
  }
518
+ const message = typeof resp?.message === 'string'
519
+ ? resp.message
520
+ : (typeof resp?.error === 'string' ? resp.error : 'Animation asset creation failed');
521
+ const error = typeof resp?.error === 'string' ? resp.error : message;
522
+ return { success: false, message, error };
523
+ }
524
+ catch (err) {
525
+ const error = String(err);
526
+ return {
527
+ success: false,
528
+ message: `Failed to create animation asset: ${error}`,
529
+ error
530
+ };
131
531
  }
132
532
  }
133
- await this.bridge.executeConsoleCommands(commands);
134
533
  return {
135
- success: true,
136
- message: `Control Rig ${assetName} created`,
137
- path: fullPath
534
+ success: false,
535
+ message: 'Automation bridge not connected for createAnimationAsset',
536
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
138
537
  };
139
538
  }
140
539
  catch (err) {
141
- return { success: false, error: `Failed to setup control rig: ${err}` };
540
+ const error = String(err);
541
+ return {
542
+ success: false,
543
+ message: `Failed to create animation asset: ${error}`,
544
+ error
545
+ };
546
+ }
547
+ }
548
+ async addNotify(params) {
549
+ try {
550
+ const rawPath = (params.animationPath || params.assetPath || '').trim();
551
+ if (!rawPath) {
552
+ const error = 'animationPath or assetPath is required for addNotify';
553
+ return { success: false, message: error, error };
554
+ }
555
+ const notifyName = (params.notifyName || 'Notify').trim();
556
+ const time = typeof params.time === 'number' && params.time >= 0 ? params.time : 0;
557
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
558
+ try {
559
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
560
+ action: 'add_notify',
561
+ assetPath: rawPath,
562
+ notifyName,
563
+ time
564
+ }), { timeoutMs: 60000 });
565
+ const result = resp?.result ?? resp;
566
+ const resultObj = result && typeof result === 'object' ? result : undefined;
567
+ if (resp && resp.success !== false && resultObj) {
568
+ return {
569
+ success: true,
570
+ message: resp.message || `Notify '${notifyName}' added to ${rawPath} at time ${time}`,
571
+ assetPath: typeof resultObj.assetPath === 'string' ? resultObj.assetPath : rawPath,
572
+ notifyName,
573
+ time
574
+ };
575
+ }
576
+ }
577
+ catch (err) {
578
+ const error = String(err);
579
+ return {
580
+ success: false,
581
+ message: `Failed to add notify: ${error}`,
582
+ error
583
+ };
584
+ }
585
+ }
586
+ return {
587
+ success: false,
588
+ message: 'Automation bridge not connected for addNotify',
589
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
590
+ };
591
+ }
592
+ catch (err) {
593
+ const error = String(err);
594
+ return {
595
+ success: false,
596
+ message: `Failed to add notify: ${error}`,
597
+ error
598
+ };
142
599
  }
143
600
  }
144
601
  async createLevelSequence(params) {
@@ -179,380 +636,22 @@ export class AnimationTools {
179
636
  }
180
637
  async playAnimation(params) {
181
638
  try {
182
- const script = this.buildPlayAnimationScript({
639
+ const commands = [
640
+ `PlayAnimation ${params.actorName} ${params.animationType} ${params.animationPath} ${params.playRate ?? 1.0} ${params.loop ?? false} ${params.blendInTime ?? 0.25} ${params.blendOutTime ?? 0.25}`
641
+ ];
642
+ await this.bridge.executeConsoleCommands(commands);
643
+ return {
644
+ success: true,
645
+ message: `Animation ${params.animationType} triggered on ${params.actorName}`,
183
646
  actorName: params.actorName,
184
647
  animationType: params.animationType,
185
- animationPath: params.animationPath,
186
- playRate: params.playRate ?? 1.0,
187
- loop: params.loop ?? false,
188
- blendInTime: params.blendInTime ?? 0.25,
189
- blendOutTime: params.blendOutTime ?? 0.25
190
- });
191
- const response = await this.bridge.executePython(script);
192
- const interpreted = interpretStandardResult(response, {
193
- successMessage: `Animation ${params.animationType} triggered on ${params.actorName}`,
194
- failureMessage: `Failed to play animation on ${params.actorName}`
195
- });
196
- const payload = interpreted.payload ?? {};
197
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
198
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
199
- const availableActors = coerceStringArray(payload.availableActors);
200
- const actorName = coerceString(payload.actorName) ?? params.actorName;
201
- const animationType = coerceString(payload.animationType) ?? params.animationType;
202
- const assetPath = coerceString(payload.assetPath) ?? params.animationPath;
203
- const errorMessage = coerceString(payload.error) ?? interpreted.error ?? `Animation playback failed for ${params.actorName}`;
204
- if (interpreted.success) {
205
- const result = {
206
- success: true,
207
- message: interpreted.message
208
- };
209
- if (warnings && warnings.length > 0) {
210
- result.warnings = warnings;
211
- }
212
- if (details && details.length > 0) {
213
- result.details = details;
214
- }
215
- if (actorName) {
216
- result.actorName = actorName;
217
- }
218
- if (animationType) {
219
- result.animationType = animationType;
220
- }
221
- if (assetPath) {
222
- result.assetPath = assetPath;
223
- }
224
- return result;
225
- }
226
- const failure = {
227
- success: false,
228
- message: `Failed to play animation: ${errorMessage}`,
229
- error: errorMessage
648
+ assetPath: params.animationPath
230
649
  };
231
- if (warnings && warnings.length > 0) {
232
- failure.warnings = warnings;
233
- }
234
- if (details && details.length > 0) {
235
- failure.details = details;
236
- }
237
- if (availableActors && availableActors.length > 0) {
238
- failure.availableActors = availableActors;
239
- }
240
- if (actorName) {
241
- failure.actorName = actorName;
242
- }
243
- if (animationType) {
244
- failure.animationType = animationType;
245
- }
246
- if (assetPath) {
247
- failure.assetPath = assetPath;
248
- }
249
- return failure;
250
650
  }
251
651
  catch (err) {
252
652
  const error = `Failed to play animation: ${err}`;
253
653
  return { success: false, message: error, error: String(err) };
254
654
  }
255
655
  }
256
- buildCreateAnimationBlueprintScript(args) {
257
- const payload = JSON.stringify(args);
258
- return `
259
- import unreal
260
- import json
261
- import traceback
262
-
263
- params = json.loads(${JSON.stringify(payload)})
264
-
265
- result = {
266
- "success": False,
267
- "message": "",
268
- "error": "",
269
- "warnings": [],
270
- "details": [],
271
- "exists": False,
272
- "skeleton": params.get("skeletonPath") or ""
273
- }
274
-
275
- try:
276
- asset_path = (params.get("path") or "/Game").rstrip('/')
277
- asset_name = params.get("name") or ""
278
- full_path = f"{asset_path}/{asset_name}"
279
- result["path"] = full_path
280
-
281
- editor_lib = unreal.EditorAssetLibrary
282
- asset_subsystem = None
283
- try:
284
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
285
- except Exception:
286
- asset_subsystem = None
287
-
288
- skeleton_path = params.get("skeletonPath")
289
- skeleton_asset = None
290
- if skeleton_path:
291
- if editor_lib.does_asset_exist(skeleton_path):
292
- skeleton_asset = editor_lib.load_asset(skeleton_path)
293
- if skeleton_asset and isinstance(skeleton_asset, unreal.Skeleton):
294
- result["details"].append(f"Using skeleton: {skeleton_path}")
295
- result["skeleton"] = skeleton_path
296
- else:
297
- result["error"] = f"Skeleton asset invalid at {skeleton_path}"
298
- result["warnings"].append(result["error"])
299
- skeleton_asset = None
300
- else:
301
- result["error"] = f"Skeleton not found at {skeleton_path}"
302
- result["warnings"].append(result["error"])
303
-
304
- if not skeleton_asset:
305
- raise RuntimeError(result["error"] or f"Skeleton {skeleton_path} unavailable")
306
-
307
- does_exist = False
308
- try:
309
- if asset_subsystem and hasattr(asset_subsystem, 'does_asset_exist'):
310
- does_exist = asset_subsystem.does_asset_exist(full_path)
311
- else:
312
- does_exist = editor_lib.does_asset_exist(full_path)
313
- except Exception:
314
- does_exist = editor_lib.does_asset_exist(full_path)
315
-
316
- if does_exist:
317
- result["exists"] = True
318
- loaded = editor_lib.load_asset(full_path)
319
- if loaded:
320
- result["success"] = True
321
- result["message"] = f"Animation Blueprint already exists at {full_path}"
322
- result["details"].append(result["message"])
323
- else:
324
- result["error"] = f"Asset exists but could not be loaded: {full_path}"
325
- result["warnings"].append(result["error"])
326
- else:
327
- factory = unreal.AnimBlueprintFactory()
328
- if skeleton_asset:
329
- try:
330
- factory.target_skeleton = skeleton_asset
331
- except Exception as assign_error:
332
- result["warnings"].append(f"Unable to assign skeleton {skeleton_path}: {assign_error}")
333
-
334
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
335
- created = asset_tools.create_asset(
336
- asset_name=asset_name,
337
- package_path=asset_path,
338
- asset_class=unreal.AnimBlueprint,
339
- factory=factory
340
- )
341
-
342
- if created:
343
- editor_lib.save_asset(full_path, only_if_is_dirty=False)
344
- result["success"] = True
345
- result["message"] = f"Animation Blueprint created at {full_path}"
346
- result["details"].append(result["message"])
347
- else:
348
- result["error"] = f"Failed to create Animation Blueprint {asset_name}"
349
-
350
- except Exception as exc:
351
- result["error"] = str(exc)
352
- result["warnings"].append(result["error"])
353
- tb = traceback.format_exc()
354
- if tb:
355
- result.setdefault("details", []).append(tb)
356
-
357
- if result["success"] and not result.get("message"):
358
- result["message"] = f"Animation Blueprint created at {result.get('path')}"
359
-
360
- if not result["success"] and not result.get("error"):
361
- result["error"] = "Animation Blueprint creation failed"
362
-
363
- if not result.get("warnings"):
364
- result.pop("warnings", None)
365
- if not result.get("details"):
366
- result.pop("details", None)
367
- if not result.get("error"):
368
- result.pop("error", None)
369
-
370
- print('RESULT:' + json.dumps(result))
371
- `.trim();
372
- }
373
- parseAnimationBlueprintResponse(response, assetName, assetPath) {
374
- const interpreted = interpretStandardResult(response, {
375
- successMessage: `Animation Blueprint ${assetName} created`,
376
- failureMessage: `Failed to create Animation Blueprint ${assetName}`
377
- });
378
- const payload = interpreted.payload ?? {};
379
- const path = coerceString(payload.path) ?? `${assetPath}/${assetName}`;
380
- const exists = coerceBoolean(payload.exists);
381
- const skeleton = coerceString(payload.skeleton);
382
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
383
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
384
- if (interpreted.success) {
385
- const result = {
386
- success: true,
387
- message: interpreted.message,
388
- path
389
- };
390
- if (typeof exists === 'boolean') {
391
- result.exists = exists;
392
- }
393
- if (skeleton) {
394
- result.skeleton = skeleton;
395
- }
396
- if (warnings && warnings.length > 0) {
397
- result.warnings = warnings;
398
- }
399
- if (details && details.length > 0) {
400
- result.details = details;
401
- }
402
- return result;
403
- }
404
- const errorMessage = coerceString(payload.error) ?? interpreted.error ?? interpreted.message;
405
- const failure = {
406
- success: false,
407
- message: `Failed to create Animation Blueprint: ${errorMessage}`,
408
- error: errorMessage,
409
- path
410
- };
411
- if (typeof exists === 'boolean') {
412
- failure.exists = exists;
413
- }
414
- if (skeleton) {
415
- failure.skeleton = skeleton;
416
- }
417
- if (warnings && warnings.length > 0) {
418
- failure.warnings = warnings;
419
- }
420
- if (details && details.length > 0) {
421
- failure.details = details;
422
- }
423
- return failure;
424
- }
425
- buildPlayAnimationScript(args) {
426
- const payload = JSON.stringify(args);
427
- return `
428
- import unreal
429
- import json
430
- import traceback
431
-
432
- params = json.loads(${JSON.stringify(payload)})
433
-
434
- result = {
435
- "success": False,
436
- "message": "",
437
- "error": "",
438
- "warnings": [],
439
- "details": [],
440
- "actorName": params.get("actorName"),
441
- "animationType": params.get("animationType"),
442
- "assetPath": params.get("animationPath"),
443
- "availableActors": []
444
- }
445
-
446
- try:
447
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
448
- actors = actor_subsystem.get_all_level_actors() if actor_subsystem else []
449
- target = None
450
- search = params.get("actorName") or ""
451
- search_lower = search.lower()
452
-
453
- for actor in actors:
454
- if not actor:
455
- continue
456
- name = (actor.get_name() or "").lower()
457
- label = (actor.get_actor_label() or "").lower()
458
- if search_lower and (search_lower == name or search_lower == label or search_lower in label):
459
- target = actor
460
- break
461
-
462
- if not target:
463
- result["error"] = f"Actor not found: {search}"
464
- result["warnings"].append("Actor search yielded no results")
465
- suggestions = []
466
- for actor in actors[:20]:
467
- try:
468
- suggestions.append(actor.get_actor_label())
469
- except Exception:
470
- continue
471
- if suggestions:
472
- result["availableActors"] = suggestions
473
- else:
474
- try:
475
- display_name = target.get_actor_label() or target.get_name()
476
- if display_name:
477
- result["actorName"] = display_name
478
- except Exception:
479
- pass
480
-
481
- skeletal_component = target.get_component_by_class(unreal.SkeletalMeshComponent)
482
- if not skeletal_component:
483
- try:
484
- skeletal_component = target.get_editor_property('mesh')
485
- except Exception:
486
- skeletal_component = None
487
-
488
- if not skeletal_component:
489
- result["error"] = "No SkeletalMeshComponent found on actor"
490
- result["warnings"].append("Actor lacks SkeletalMeshComponent")
491
- else:
492
- asset_path = params.get("animationPath")
493
- if not asset_path or not unreal.EditorAssetLibrary.does_asset_exist(asset_path):
494
- result["error"] = f"Animation asset not found: {asset_path}"
495
- result["warnings"].append("Animation asset missing")
496
- else:
497
- asset = unreal.EditorAssetLibrary.load_asset(asset_path)
498
- anim_type = params.get("animationType") or ""
499
- if anim_type == 'Montage':
500
- anim_instance = skeletal_component.get_anim_instance()
501
- if anim_instance:
502
- try:
503
- anim_instance.montage_play(asset, params.get("playRate", 1.0))
504
- result["success"] = True
505
- result["message"] = f"Montage playing on {result.get('actorName') or search}"
506
- result["details"].append(result["message"])
507
- except Exception as play_error:
508
- result["error"] = f"Failed to play montage: {play_error}"
509
- result["warnings"].append(result["error"])
510
- else:
511
- result["error"] = "AnimInstance not found on SkeletalMeshComponent"
512
- result["warnings"].append(result["error"])
513
- elif anim_type == 'Sequence':
514
- try:
515
- skeletal_component.play_animation(asset, bool(params.get("loop")))
516
- try:
517
- anim_instance = skeletal_component.get_anim_instance()
518
- if anim_instance:
519
- anim_instance.set_play_rate(params.get("playRate", 1.0))
520
- except Exception:
521
- pass
522
- result["success"] = True
523
- result["message"] = f"Sequence playing on {result.get('actorName') or search}"
524
- result["details"].append(result["message"])
525
- except Exception as play_error:
526
- result["error"] = f"Failed to play sequence: {play_error}"
527
- result["warnings"].append(result["error"])
528
- else:
529
- result["error"] = "BlendSpace playback requires Animation Blueprint support"
530
- result["warnings"].append("Unsupported animation type for direct play")
531
-
532
- except Exception as exc:
533
- result["error"] = str(exc)
534
- result["warnings"].append(result["error"])
535
- tb = traceback.format_exc()
536
- if tb:
537
- result["details"].append(tb)
538
-
539
- if result["success"] and not result.get("message"):
540
- result["message"] = f"Animation {result.get('animationType')} triggered on {result.get('actorName') or params.get('actorName')}"
541
-
542
- if not result["success"] and not result.get("error"):
543
- result["error"] = "Animation playback failed"
544
-
545
- if not result.get("warnings"):
546
- result.pop("warnings", None)
547
- if not result.get("details"):
548
- result.pop("details", None)
549
- if not result.get("availableActors"):
550
- result.pop("availableActors", None)
551
- if not result.get("error"):
552
- result.pop("error", None)
553
-
554
- print('RESULT:' + json.dumps(result))
555
- `.trim();
556
- }
557
656
  }
558
657
  //# sourceMappingURL=animation.js.map