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,10 +1,9 @@
1
1
  import { ensureRotation, ensureVector3 } from '../utils/validation.js';
2
- import { coerceString, coerceVector3, interpretStandardResult } from '../utils/result-helpers.js';
3
- import { escapePythonString } from '../utils/python.js';
4
- export class ActorTools {
5
- bridge;
2
+ import { BaseTool } from './base-tool.js';
3
+ import { wasmIntegration } from '../wasm/index.js';
4
+ export class ActorTools extends BaseTool {
6
5
  constructor(bridge) {
7
- this.bridge = bridge;
6
+ super(bridge);
8
7
  }
9
8
  async spawn(params) {
10
9
  if (!params.classPath || typeof params.classPath !== 'string' || params.classPath.trim().length === 0) {
@@ -28,305 +27,123 @@ export class ActorTools {
28
27
  const mappedClassPath = shapeMapping[lowerName] ?? this.resolveActorClass(className);
29
28
  const [locX, locY, locZ] = ensureVector3(params.location ?? { x: 0, y: 0, z: 100 }, 'actor location');
30
29
  const [rotPitch, rotYaw, rotRoll] = ensureRotation(params.rotation ?? { pitch: 0, yaw: 0, roll: 0 }, 'actor rotation');
31
- const escapedResolvedClassPath = escapePythonString(mappedClassPath);
32
- const escapedRequestedPath = escapePythonString(className);
33
- const escapedRequestedActorName = sanitizedActorName ? escapePythonString(sanitizedActorName) : '';
34
- const pythonCmd = `
35
- import unreal
36
- import json
37
- import time
38
-
39
- result = {
40
- "success": False,
41
- "message": "",
42
- "error": "",
43
- "actorName": "",
44
- "requestedClass": "${escapedRequestedPath}",
45
- "resolvedClass": "${escapedResolvedClassPath}",
46
- "location": [${locX}, ${locY}, ${locZ}],
47
- "rotation": [${rotPitch}, ${rotYaw}, ${rotRoll}],
48
- "requestedActorName": "${escapedRequestedActorName}",
49
- "warnings": [],
50
- "details": []
51
- }
52
-
53
- ${this.getPythonSpawnHelper()}
54
-
55
- abstract_classes = ['PlaneReflectionCapture', 'ReflectionCapture', 'Actor', 'Pawn', 'Character']
56
-
57
- def finalize():
58
- data = dict(result)
59
- if data.get("success"):
60
- if not data.get("message"):
61
- data["message"] = "Actor spawned successfully"
62
- data.pop("error", None)
63
- else:
64
- if not data.get("error"):
65
- data["error"] = data.get("message") or "Failed to spawn actor"
66
- if not data.get("message"):
67
- data["message"] = data["error"]
68
- if not data.get("warnings"):
69
- data.pop("warnings", None)
70
- if not data.get("details"):
71
- data.pop("details", None)
72
- return data
73
-
74
- try:
75
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
76
- if les and les.is_in_play_in_editor():
77
- result["message"] = "Cannot spawn actors while in Play In Editor mode. Please stop PIE first."
78
- result["error"] = result["message"]
79
- result["details"].append("Play In Editor mode detected")
80
- print('RESULT:' + json.dumps(finalize()))
81
- raise SystemExit(0)
82
- except SystemExit:
83
- raise
84
- except Exception:
85
- result["warnings"].append("Unable to determine Play In Editor state")
86
-
87
- if result["requestedClass"] in abstract_classes:
88
- result["message"] = f"Cannot spawn {result['requestedClass']}: class is abstract and cannot be instantiated"
89
- result["error"] = result["message"]
90
- else:
91
- try:
92
- class_path = result["resolvedClass"]
93
- requested_path = result["requestedClass"]
94
- location = unreal.Vector(${locX}, ${locY}, ${locZ})
95
- rotation = unreal.Rotator(${rotPitch}, ${rotYaw}, ${rotRoll})
96
- actor = None
97
-
98
- simple_name = requested_path.split('/')[-1] if '/' in requested_path else requested_path
99
- if '.' in simple_name:
100
- simple_name = simple_name.split('.')[-1]
101
- simple_name_lower = simple_name.lower()
102
- class_lookup_name = class_path.split('.')[-1] if '.' in class_path else simple_name
103
-
104
- result["details"].append(f"Attempting spawn using class path: {class_path}")
105
-
106
- if class_path.startswith('/Game') or class_path.startswith('/Engine'):
107
- try:
108
- asset = unreal.EditorAssetLibrary.load_asset(class_path)
109
- except Exception as asset_error:
110
- asset = None
111
- result["warnings"].append(f"Failed to load asset for {class_path}: {asset_error}")
112
- if asset:
113
- if isinstance(asset, unreal.Blueprint):
114
- try:
115
- actor_class = asset.generated_class()
116
- except Exception as blueprint_error:
117
- actor_class = None
118
- result["warnings"].append(f"Failed to resolve blueprint class: {blueprint_error}")
119
- if actor_class:
120
- actor = spawn_actor_from_class(actor_class, location, rotation)
121
- if actor:
122
- result["details"].append("Spawned using Blueprint generated class")
123
- elif isinstance(asset, unreal.StaticMesh):
124
- actor = spawn_actor_from_class(unreal.StaticMeshActor, location, rotation)
125
- if actor:
126
- mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
127
- if mesh_component:
128
- mesh_component.set_static_mesh(asset)
129
- mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
130
- result["details"].append("Applied static mesh to spawned StaticMeshActor")
131
-
132
- if not actor:
133
- shape_map = {
134
- 'cube': '/Engine/BasicShapes/Cube',
135
- 'sphere': '/Engine/BasicShapes/Sphere',
136
- 'cylinder': '/Engine/BasicShapes/Cylinder',
137
- 'cone': '/Engine/BasicShapes/Cone',
138
- 'plane': '/Engine/BasicShapes/Plane',
139
- 'torus': '/Engine/BasicShapes/Torus'
140
- }
141
- mesh_path = shape_map.get(simple_name_lower)
142
- if not mesh_path and class_path.startswith('/Engine/BasicShapes'):
143
- mesh_path = class_path
144
- if mesh_path:
145
- try:
146
- shape_mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
147
- except Exception as shape_error:
148
- shape_mesh = None
149
- result["warnings"].append(f"Failed to load shape mesh {mesh_path}: {shape_error}")
150
- if shape_mesh:
151
- actor = spawn_actor_from_class(unreal.StaticMeshActor, location, rotation)
152
- if actor:
153
- mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
154
- if mesh_component:
155
- mesh_component.set_static_mesh(shape_mesh)
156
- mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
157
- result["details"].append(f"Spawned StaticMeshActor with mesh {mesh_path}")
158
-
159
- if not actor:
160
- if class_lookup_name == "StaticMeshActor":
161
- actor = spawn_actor_from_class(unreal.StaticMeshActor, location, rotation)
162
- if actor:
163
- try:
164
- cube_mesh = unreal.EditorAssetLibrary.load_asset('/Engine/BasicShapes/Cube')
165
- except Exception as cube_error:
166
- cube_mesh = None
167
- result["warnings"].append(f"Failed to load default cube mesh: {cube_error}")
168
- if cube_mesh:
169
- mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
170
- if mesh_component:
171
- mesh_component.set_static_mesh(cube_mesh)
172
- mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
173
- result["details"].append("Applied default cube mesh to StaticMeshActor")
174
- elif class_lookup_name == "CameraActor":
175
- actor = spawn_actor_from_class(unreal.CameraActor, location, rotation)
176
- if actor:
177
- result["details"].append("Spawned CameraActor via reflected class lookup")
178
- else:
179
- actor_class = getattr(unreal, class_lookup_name, None)
180
- if actor_class:
181
- actor = spawn_actor_from_class(actor_class, location, rotation)
182
- if actor:
183
- result["details"].append(f"Spawned {class_lookup_name} via reflected class lookup")
184
-
185
- if actor:
186
- desired_name = (result.get("requestedActorName") or "").strip()
187
- actor_name = ""
188
- if desired_name:
189
- try:
190
- try:
191
- actor.set_actor_label(desired_name, True)
192
- except TypeError:
193
- actor.set_actor_label(desired_name)
194
- actor_name = actor.get_actor_label() or desired_name
195
- except Exception as label_error:
196
- result["warnings"].append(f"Failed to honor requested actor name '{desired_name}': {label_error}")
197
- if not actor_name:
198
- timestamp = int(time.time() * 1000) % 10000
199
- base_name = simple_name or class_lookup_name or class_path.split('/')[-1]
200
- fallback_name = f"{base_name}_{timestamp}"
201
- try:
202
- actor.set_actor_label(fallback_name)
203
- except Exception as label_error:
204
- result["warnings"].append(f"Failed to set actor label: {label_error}")
205
- actor_name = actor.get_actor_label() or fallback_name
206
- result["success"] = True
207
- result["actorName"] = actor_name
208
- if not result["message"]:
209
- result["message"] = f"Spawned {actor_name} at ({location.x}, {location.y}, {location.z})"
210
- else:
211
- result["message"] = f"Failed to spawn actor from: {class_path}. Try using /Engine/BasicShapes/Cube or StaticMeshActor"
212
- result["error"] = result["message"]
213
- except Exception as spawn_error:
214
- result["error"] = f"Error spawning actor: {spawn_error}"
215
- if not result["message"]:
216
- result["message"] = result["error"]
217
-
218
- print('RESULT:' + json.dumps(finalize()))
219
- `.trim();
220
30
  try {
221
- const response = await this.bridge.executePython(pythonCmd);
222
- const interpreted = interpretStandardResult(response, {
223
- successMessage: `Spawned actor ${className}`,
224
- failureMessage: `Failed to spawn actor ${className}`
225
- });
226
- if (!interpreted.success) {
227
- throw new Error(interpreted.error || interpreted.message);
31
+ const bridge = this.getAutomationBridge();
32
+ const timeoutMs = typeof params.timeoutMs === 'number' && params.timeoutMs > 0 ? params.timeoutMs : undefined;
33
+ const response = await bridge.sendAutomationRequest('control_actor', {
34
+ action: 'spawn',
35
+ classPath: mappedClassPath,
36
+ location: { x: locX, y: locY, z: locZ },
37
+ rotation: { pitch: rotPitch, yaw: rotYaw, roll: rotRoll },
38
+ actorName: sanitizedActorName,
39
+ meshPath: params.meshPath
40
+ }, timeoutMs ? { timeoutMs } : undefined);
41
+ if (!response || !response.success) {
42
+ const error = response?.error;
43
+ const errorMsg = typeof error === 'string' ? error : error?.message || response?.message || 'Failed to spawn actor';
44
+ throw new Error(errorMsg);
228
45
  }
229
- const actorName = coerceString(interpreted.payload.actorName);
230
- const resolvedClass = coerceString(interpreted.payload.resolvedClass) ?? mappedClassPath;
231
- const requestedClass = coerceString(interpreted.payload.requestedClass) ?? className;
232
- const locationVector = coerceVector3(interpreted.payload.location) ?? [locX, locY, locZ];
233
- const rotationVector = coerceVector3(interpreted.payload.rotation) ?? [rotPitch, rotYaw, rotRoll];
46
+ const data = response.data || {};
234
47
  const result = {
235
48
  success: true,
236
- message: interpreted.message,
237
- actorName: actorName ?? undefined,
238
- resolvedClass,
239
- requestedClass,
240
- location: { x: locationVector[0], y: locationVector[1], z: locationVector[2] },
241
- rotation: { pitch: rotationVector[0], yaw: rotationVector[1], roll: rotationVector[2] }
49
+ message: response.message || `Spawned actor ${className}`,
50
+ actorName: data.name || response.actorName,
51
+ actorPath: data.objectPath || response.actorPath,
52
+ resolvedClass: mappedClassPath,
53
+ requestedClass: className,
54
+ location: { x: locX, y: locY, z: locZ },
55
+ rotation: { pitch: rotPitch, yaw: rotYaw, roll: rotRoll },
56
+ data: data,
57
+ actor: {
58
+ name: data.name || response.actorName,
59
+ path: data.objectPath || response.actorPath || mappedClassPath
60
+ }
242
61
  };
243
- if (interpreted.warnings?.length) {
244
- result.warnings = interpreted.warnings;
62
+ if (response.warnings?.length) {
63
+ result.warnings = response.warnings;
245
64
  }
246
- if (interpreted.details?.length) {
247
- result.details = interpreted.details;
65
+ if (response.details?.length) {
66
+ result.details = response.details;
67
+ }
68
+ if (response.componentPaths?.length) {
69
+ result.componentPaths = response.componentPaths;
248
70
  }
249
71
  return result;
250
72
  }
251
73
  catch (err) {
252
- throw new Error(`Failed to spawn actor via Python: ${err}`);
74
+ throw new Error(`Failed to spawn actor: ${err}`);
253
75
  }
254
76
  }
255
- async spawnViaConsole(params) {
256
- try {
257
- const [locX, locY, locZ] = ensureVector3(params.location ?? { x: 0, y: 0, z: 100 }, 'actor location');
258
- // Check if editor is in play mode first
259
- try {
260
- const pieCheckPython = `
261
- import unreal
262
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
263
- if les and les.is_in_play_in_editor():
264
- print("PIE_ACTIVE")
265
- else:
266
- print("PIE_INACTIVE")
267
- `.trim();
268
- const pieCheckResult = await this.bridge.executePython(pieCheckPython);
269
- const outputStr = typeof pieCheckResult === 'string' ? pieCheckResult : JSON.stringify(pieCheckResult);
270
- if (outputStr.includes('PIE_ACTIVE')) {
271
- throw new Error('Cannot spawn actors while in Play In Editor mode. Please stop PIE first.');
272
- }
77
+ async delete(params) {
78
+ if (params.actorNames && Array.isArray(params.actorNames)) {
79
+ const names = params.actorNames
80
+ .filter(name => typeof name === 'string')
81
+ .map(name => name.trim())
82
+ .filter(name => name.length > 0);
83
+ if (names.length === 0) {
84
+ return {
85
+ success: true,
86
+ message: 'No actors provided for deletion; no-op',
87
+ deleted: [],
88
+ noOp: true
89
+ };
273
90
  }
274
- catch (pieErr) {
275
- // If the error is about PIE, throw it
276
- if (String(pieErr).includes('Play In Editor')) {
277
- throw pieErr;
278
- }
279
- // Otherwise ignore and continue
91
+ const bridge = this.getAutomationBridge();
92
+ const response = await bridge.sendAutomationRequest('control_actor', {
93
+ action: 'delete',
94
+ actorNames: names
95
+ });
96
+ const result = (response?.data || response?.result || response) ?? {};
97
+ const deleted = result.deleted ?? names;
98
+ const missing = result.missing ?? [];
99
+ const errorObj = response?.error;
100
+ const errorCode = (typeof errorObj === 'object' ? errorObj.code : String(errorObj || result.error || '')).toUpperCase();
101
+ if (response && response.success === false && errorCode === 'DELETE_PARTIAL') {
102
+ return {
103
+ success: true,
104
+ message: errorObj?.message || response.message || 'Some actors could not be deleted',
105
+ deleted,
106
+ missing,
107
+ partial: true
108
+ };
280
109
  }
281
- // List of known abstract classes that cannot be spawned
282
- const abstractClasses = ['PlaneReflectionCapture', 'ReflectionCapture', 'Actor'];
283
- // Check if this is an abstract class
284
- if (abstractClasses.includes(params.classPath)) {
285
- throw new Error(`Cannot spawn ${params.classPath}: class is abstract and cannot be instantiated`);
110
+ if (!response || response.success === false) {
111
+ throw new Error(errorObj?.message || response?.message || 'Failed to delete actors');
286
112
  }
287
- // Get the console-friendly class name
288
- const spawnClass = this.getConsoleClassName(params.classPath);
289
- // Use summon command with location if provided
290
- const command = `summon ${spawnClass} ${locX} ${locY} ${locZ}`;
291
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
292
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
293
- functionName: 'ExecuteConsoleCommand',
294
- parameters: {
295
- WorldContextObject: null,
296
- Command: command,
297
- SpecificPlayer: null
298
- },
299
- generateTransaction: false
300
- });
301
- // Console commands don't reliably report success/failure
302
- // We can't guarantee this actually worked, so indicate uncertainty
303
113
  return {
304
114
  success: true,
305
- message: `Actor spawn attempted via console: ${spawnClass} at ${locX},${locY},${locZ}`,
306
- note: 'Console spawn result uncertain - verify in editor'
115
+ message: response.message || 'Deleted actors',
116
+ deleted: result.deleted || deleted,
117
+ ...result
307
118
  };
308
119
  }
309
- catch (err) {
310
- throw new Error(`Failed to spawn actor: ${err}`);
120
+ if (!params.actorName || typeof params.actorName !== 'string') {
121
+ throw new Error('Invalid actorName');
311
122
  }
123
+ return this.sendRequest('delete', { actorName: params.actorName }, 'control_actor');
312
124
  }
313
- getPythonSpawnHelper() {
314
- return `
315
- def spawn_actor_from_class(actor_class, location, rotation):
316
- actor = None
317
- try:
318
- actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
319
- if actor_subsys:
320
- actor = actor_subsys.spawn_actor_from_class(actor_class, location, rotation)
321
- except Exception:
322
- actor = None
323
- if not actor:
324
- raise RuntimeError('EditorActorSubsystem unavailable or failed to spawn actor. Enable Editor Scripting Utilities plugin and verify class path.')
325
- return actor
326
- `.trim();
125
+ async applyForce(params) {
126
+ if (!params.actorName || typeof params.actorName !== 'string') {
127
+ throw new Error('Invalid actorName');
128
+ }
129
+ if (!params.force || typeof params.force !== 'object') {
130
+ throw new Error('Invalid force vector');
131
+ }
132
+ const [forceX, forceY, forceZ] = ensureVector3(params.force, 'force vector');
133
+ if (forceX === 0 && forceY === 0 && forceZ === 0) {
134
+ return {
135
+ success: true,
136
+ message: `Zero force provided for ${params.actorName}; no-op`,
137
+ physicsEnabled: false,
138
+ noOp: true
139
+ };
140
+ }
141
+ return this.sendRequest('apply_force', {
142
+ actorName: params.actorName,
143
+ force: { x: forceX, y: forceY, z: forceZ }
144
+ }, 'control_actor');
327
145
  }
328
146
  resolveActorClass(classPath) {
329
- // Map common names to full Unreal class paths
330
147
  const classMap = {
331
148
  'PointLight': '/Script/Engine.PointLight',
332
149
  'DirectionalLight': '/Script/Engine.DirectionalLight',
@@ -349,55 +166,283 @@ def spawn_actor_from_class(actor_class, location, rotation):
349
166
  'AtmosphericFog': '/Script/Engine.AtmosphericFog',
350
167
  'SphereReflectionCapture': '/Script/Engine.SphereReflectionCapture',
351
168
  'BoxReflectionCapture': '/Script/Engine.BoxReflectionCapture',
352
- // PlaneReflectionCapture is abstract and cannot be spawned
353
169
  'DecalActor': '/Script/Engine.DecalActor'
354
170
  };
355
- // Check if it's a simple name that needs mapping
356
171
  if (classMap[classPath]) {
357
172
  return classMap[classPath];
358
173
  }
359
- // Check if it already looks like a full path
360
174
  if (classPath.startsWith('/Script/') || classPath.startsWith('/Game/')) {
361
175
  return classPath;
362
176
  }
363
177
  if (classPath.startsWith('/Engine/')) {
364
178
  return classPath;
365
179
  }
366
- // Check for Blueprint paths
367
180
  if (classPath.includes('Blueprint') || classPath.includes('BP_')) {
368
- // Ensure it has the proper prefix
369
181
  if (!classPath.startsWith('/Game/')) {
370
182
  return '/Game/' + classPath;
371
183
  }
372
184
  return classPath;
373
185
  }
374
- // Default: assume it's an engine class
375
186
  return '/Script/Engine.' + classPath;
376
187
  }
377
- getConsoleClassName(classPath) {
378
- // Normalize class path for console 'summon'
379
- const input = classPath;
380
- // Engine classes: reduce '/Script/Engine.ClassName' to 'ClassName'
381
- if (input.startsWith('/Script/Engine.')) {
382
- return input.replace('/Script/Engine.', '');
383
- }
384
- // If it's already a simple class name (no path) and not a /Game asset, strip optional _C and return
385
- if (!input.startsWith('/Game/') && !input.includes('/')) {
386
- if (input.endsWith('_C'))
387
- return input.slice(0, -2);
388
- return input;
389
- }
390
- // Blueprint assets under /Game: ensure '/Game/Path/Asset.Asset_C'
391
- if (input.startsWith('/Game/')) {
392
- // Remove any existing ".Something" suffix to rebuild normalized class ref
393
- const pathWithoutSuffix = input.split('.')[0];
394
- const parts = pathWithoutSuffix.split('/');
395
- const assetName = parts[parts.length - 1].replace(/_C$/, '');
396
- const normalized = `${pathWithoutSuffix}.${assetName}_C`;
397
- return normalized;
398
- }
399
- // Fallback: return input unchanged
400
- return input;
188
+ async spawnBlueprint(params) {
189
+ const blueprintPath = typeof params.blueprintPath === 'string' ? params.blueprintPath.trim() : '';
190
+ if (!blueprintPath) {
191
+ throw new Error('Invalid blueprintPath');
192
+ }
193
+ const actorName = typeof params.actorName === 'string' && params.actorName.trim().length > 0 ? params.actorName.trim() : undefined;
194
+ const location = params.location ? ensureVector3(params.location, 'spawn_blueprint location') : undefined;
195
+ const rotation = params.rotation ? ensureRotation(params.rotation, 'spawn_blueprint rotation') : undefined;
196
+ const payload = { blueprintPath };
197
+ if (actorName)
198
+ payload.actorName = actorName;
199
+ if (location)
200
+ payload.location = { x: location[0], y: location[1], z: location[2] };
201
+ if (rotation)
202
+ payload.rotation = { pitch: rotation[0], yaw: rotation[1], roll: rotation[2] };
203
+ return this.sendRequest('spawn_blueprint', payload, 'control_actor');
204
+ }
205
+ async setTransform(params) {
206
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
207
+ if (!actorName) {
208
+ throw new Error('Invalid actorName');
209
+ }
210
+ const payload = { actorName };
211
+ if (params.location) {
212
+ const loc = ensureVector3(params.location, 'set_transform location');
213
+ payload.location = { x: loc[0], y: loc[1], z: loc[2] };
214
+ }
215
+ if (params.rotation) {
216
+ const rot = ensureRotation(params.rotation, 'set_transform rotation');
217
+ payload.rotation = { pitch: rot[0], yaw: rot[1], roll: rot[2] };
218
+ }
219
+ if (params.scale) {
220
+ const scl = ensureVector3(params.scale, 'set_transform scale');
221
+ payload.scale = { x: scl[0], y: scl[1], z: scl[2] };
222
+ }
223
+ return this.sendRequest('set_transform', payload, 'control_actor');
224
+ }
225
+ async getTransform(actorName) {
226
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
227
+ throw new Error('Invalid actorName');
228
+ }
229
+ return this.sendRequest('get_transform', { actorName }, 'control_actor')
230
+ .then(response => {
231
+ return response;
232
+ });
233
+ }
234
+ async setVisibility(params) {
235
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
236
+ if (!actorName) {
237
+ throw new Error('Invalid actorName');
238
+ }
239
+ return this.sendRequest('set_visibility', { actorName, visible: Boolean(params.visible) }, 'control_actor');
240
+ }
241
+ async addComponent(params) {
242
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
243
+ const componentType = typeof params.componentType === 'string' ? params.componentType.trim() : '';
244
+ if (!actorName)
245
+ throw new Error('Invalid actorName');
246
+ if (!componentType)
247
+ throw new Error('Invalid componentType');
248
+ return this.sendRequest('add_component', {
249
+ actorName,
250
+ componentType,
251
+ componentName: typeof params.componentName === 'string' ? params.componentName : undefined,
252
+ properties: params.properties
253
+ }, 'control_actor');
254
+ }
255
+ async setComponentProperties(params) {
256
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
257
+ const componentName = typeof params.componentName === 'string' ? params.componentName.trim() : '';
258
+ if (!actorName)
259
+ throw new Error('Invalid actorName');
260
+ if (!componentName)
261
+ throw new Error('Invalid componentName');
262
+ return this.sendRequest('set_component_properties', {
263
+ actorName,
264
+ componentName,
265
+ properties: params.properties ?? {}
266
+ }, 'control_actor');
267
+ }
268
+ async getComponents(actorName) {
269
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
270
+ throw new Error('Invalid actorName');
271
+ }
272
+ const response = await this.sendRequest('get_components', { actorName }, 'control_actor');
273
+ if (!response.success) {
274
+ return { success: false, error: response.error || `Failed to get components for actor ${actorName}` };
275
+ }
276
+ const data = response.data ?? response.result ?? response;
277
+ const components = Array.isArray(data)
278
+ ? data
279
+ : (Array.isArray(data?.components) ? data.components : []);
280
+ const count = typeof data?.count === 'number' ? data.count : components.length;
281
+ return {
282
+ success: true,
283
+ message: 'Actor components retrieved',
284
+ components,
285
+ count
286
+ };
287
+ }
288
+ async duplicate(params) {
289
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
290
+ if (!actorName)
291
+ throw new Error('Invalid actorName');
292
+ const payload = { actorName };
293
+ if (typeof params.newName === 'string' && params.newName.trim().length > 0) {
294
+ payload.newName = params.newName.trim();
295
+ }
296
+ if (params.offset) {
297
+ const offs = ensureVector3(params.offset, 'duplicate offset');
298
+ const origin = [0, 0, 0];
299
+ const calculatedOffset = wasmIntegration.vectorAdd(origin, offs);
300
+ console.error('[WASM] Using vectorAdd for duplicate offset calculation');
301
+ payload.offset = { x: calculatedOffset[0], y: calculatedOffset[1], z: calculatedOffset[2] };
302
+ }
303
+ return this.sendRequest('duplicate', payload, 'control_actor');
304
+ }
305
+ async addTag(params) {
306
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
307
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
308
+ if (!actorName)
309
+ throw new Error('Invalid actorName');
310
+ if (!tag)
311
+ throw new Error('Invalid tag');
312
+ return this.sendRequest('add_tag', { actorName, tag }, 'control_actor');
313
+ }
314
+ async removeTag(params) {
315
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
316
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
317
+ if (!actorName)
318
+ throw new Error('Invalid actorName');
319
+ if (!tag)
320
+ throw new Error('Invalid tag');
321
+ return this.sendRequest('remove_tag', { actorName, tag }, 'control_actor');
322
+ }
323
+ async findByTag(params) {
324
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
325
+ if (!tag) {
326
+ return {
327
+ success: true,
328
+ message: 'Empty tag query; no actors matched',
329
+ data: {
330
+ actors: [],
331
+ count: 0
332
+ }
333
+ };
334
+ }
335
+ return this.sendRequest('find_by_tag', {
336
+ tag,
337
+ matchType: typeof params.matchType === 'string' ? params.matchType : undefined
338
+ }, 'control_actor');
339
+ }
340
+ async findByName(name) {
341
+ if (typeof name !== 'string' || name.trim().length === 0) {
342
+ throw new Error('Invalid actor name query');
343
+ }
344
+ return this.sendRequest('find_by_name', { name: name.trim() }, 'control_actor');
345
+ }
346
+ async detach(actorName) {
347
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
348
+ throw new Error('Invalid actorName');
349
+ }
350
+ return this.sendRequest('detach', { actorName }, 'control_actor');
351
+ }
352
+ async attach(params) {
353
+ const child = typeof params.childActor === 'string' ? params.childActor.trim() : '';
354
+ const parent = typeof params.parentActor === 'string' ? params.parentActor.trim() : '';
355
+ if (!child)
356
+ throw new Error('Invalid childActor');
357
+ if (!parent)
358
+ throw new Error('Invalid parentActor');
359
+ return this.sendRequest('attach', { childActor: child, parentActor: parent }, 'control_actor');
360
+ }
361
+ async deleteByTag(tag) {
362
+ if (typeof tag !== 'string' || tag.trim().length === 0) {
363
+ throw new Error('Invalid tag');
364
+ }
365
+ return this.sendRequest('delete_by_tag', { tag: tag.trim() }, 'control_actor');
366
+ }
367
+ async setBlueprintVariables(params) {
368
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
369
+ if (!actorName)
370
+ throw new Error('Invalid actorName');
371
+ return this.sendRequest('set_blueprint_variables', { actorName, variables: params.variables ?? {} }, 'control_actor');
372
+ }
373
+ async createSnapshot(params) {
374
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
375
+ const snapshotName = typeof params.snapshotName === 'string' ? params.snapshotName.trim() : '';
376
+ if (!actorName)
377
+ throw new Error('Invalid actorName');
378
+ if (!snapshotName)
379
+ throw new Error('Invalid snapshotName');
380
+ return this.sendRequest('create_snapshot', { actorName, snapshotName }, 'control_actor');
381
+ }
382
+ async restoreSnapshot(params) {
383
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
384
+ const snapshotName = typeof params.snapshotName === 'string' ? params.snapshotName.trim() : '';
385
+ if (!actorName)
386
+ throw new Error('Invalid actorName');
387
+ if (!snapshotName)
388
+ throw new Error('Invalid snapshotName');
389
+ return this.sendRequest('restore_snapshot', { actorName, snapshotName }, 'control_actor');
390
+ }
391
+ async exportActor(params) {
392
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
393
+ if (!actorName)
394
+ throw new Error('Invalid actorName');
395
+ return this.sendRequest('export', {
396
+ actorName,
397
+ destinationPath: params.destinationPath
398
+ }, 'control_actor');
399
+ }
400
+ async getBoundingBox(actorName) {
401
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
402
+ throw new Error('Invalid actorName');
403
+ }
404
+ const response = await this.sendRequest('get_bounding_box', { actorName }, 'control_actor');
405
+ if (!response.success) {
406
+ return { success: false, error: response.error || `Failed to get bounding box for actor ${actorName}` };
407
+ }
408
+ return {
409
+ success: true,
410
+ message: 'Bounding box retrieved',
411
+ boundingBox: response.data || response.result || {}
412
+ };
413
+ }
414
+ async getMetadata(actorName) {
415
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
416
+ throw new Error('Invalid actorName');
417
+ }
418
+ const response = await this.sendRequest('get_metadata', { actorName }, 'control_actor');
419
+ if (!response.success) {
420
+ return { success: false, error: response.error || `Failed to get metadata for actor ${actorName}` };
421
+ }
422
+ return {
423
+ success: true,
424
+ message: 'Actor metadata retrieved',
425
+ metadata: response.data || response.result || {}
426
+ };
427
+ }
428
+ async listActors(params) {
429
+ const payload = {};
430
+ if (params?.filter) {
431
+ payload.filter = params.filter;
432
+ }
433
+ const response = await this.sendRequest('list_actors', payload, 'control_actor');
434
+ if (!response.success) {
435
+ return { success: false, error: response.error || 'Failed to list actors' };
436
+ }
437
+ const dataObj = response.data || response.result || {};
438
+ const actorsRaw = response.actors || (dataObj && dataObj.actors) || (Array.isArray(dataObj) ? dataObj : []);
439
+ const actors = Array.isArray(actorsRaw) ? actorsRaw : [];
440
+ return {
441
+ success: true,
442
+ message: `Found ${actors.length} actors`,
443
+ actors,
444
+ count: actors.length
445
+ };
401
446
  }
402
447
  }
403
448
  //# sourceMappingURL=actors.js.map