unreal-engine-mcp-server 0.4.6 → 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 +269 -22
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -72
  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 -604
  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 +5475 -1627
  97. package/dist/tools/consolidated-tool-definitions.js +829 -482
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1009
  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 +45 -0
  161. package/dist/tools/logs.js +210 -0
  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 +195 -11
  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 -649
  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 -500
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1122
  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 +219 -0
  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 +250 -13
  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 -572
  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,10 +1,8 @@
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
+ export class ActorTools extends BaseTool {
6
4
  constructor(bridge) {
7
- this.bridge = bridge;
5
+ super(bridge);
8
6
  }
9
7
  async spawn(params) {
10
8
  if (!params.classPath || typeof params.classPath !== 'string' || params.classPath.trim().length === 0) {
@@ -28,305 +26,121 @@ export class ActorTools {
28
26
  const mappedClassPath = shapeMapping[lowerName] ?? this.resolveActorClass(className);
29
27
  const [locX, locY, locZ] = ensureVector3(params.location ?? { x: 0, y: 0, z: 100 }, 'actor location');
30
28
  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
29
  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);
30
+ const bridge = this.getAutomationBridge();
31
+ const timeoutMs = typeof params.timeoutMs === 'number' && params.timeoutMs > 0 ? params.timeoutMs : undefined;
32
+ const response = await bridge.sendAutomationRequest('control_actor', {
33
+ action: 'spawn',
34
+ classPath: mappedClassPath,
35
+ location: { x: locX, y: locY, z: locZ },
36
+ rotation: { pitch: rotPitch, yaw: rotYaw, roll: rotRoll },
37
+ actorName: sanitizedActorName,
38
+ meshPath: params.meshPath
39
+ }, timeoutMs ? { timeoutMs } : undefined);
40
+ if (!response || !response.success) {
41
+ throw new Error(response?.error || response?.message || 'Failed to spawn actor');
228
42
  }
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];
43
+ const data = response.data || {};
234
44
  const result = {
235
45
  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] }
46
+ message: response.message || `Spawned actor ${className}`,
47
+ actorName: data.name || response.actorName,
48
+ actorPath: data.objectPath || response.actorPath,
49
+ resolvedClass: mappedClassPath,
50
+ requestedClass: className,
51
+ location: { x: locX, y: locY, z: locZ },
52
+ rotation: { pitch: rotPitch, yaw: rotYaw, roll: rotRoll },
53
+ data: data,
54
+ actor: {
55
+ name: data.name || response.actorName,
56
+ path: data.objectPath || response.actorPath || mappedClassPath
57
+ }
242
58
  };
243
- if (interpreted.warnings?.length) {
244
- result.warnings = interpreted.warnings;
59
+ if (response.warnings?.length) {
60
+ result.warnings = response.warnings;
245
61
  }
246
- if (interpreted.details?.length) {
247
- result.details = interpreted.details;
62
+ if (response.details?.length) {
63
+ result.details = response.details;
64
+ }
65
+ if (response.componentPaths?.length) {
66
+ result.componentPaths = response.componentPaths;
248
67
  }
249
68
  return result;
250
69
  }
251
70
  catch (err) {
252
- throw new Error(`Failed to spawn actor via Python: ${err}`);
71
+ throw new Error(`Failed to spawn actor: ${err}`);
253
72
  }
254
73
  }
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
- }
74
+ async delete(params) {
75
+ if (params.actorNames && Array.isArray(params.actorNames)) {
76
+ const names = params.actorNames
77
+ .filter(name => typeof name === 'string')
78
+ .map(name => name.trim())
79
+ .filter(name => name.length > 0);
80
+ if (names.length === 0) {
81
+ return {
82
+ success: true,
83
+ message: 'No actors provided for deletion; no-op',
84
+ deleted: [],
85
+ noOp: true
86
+ };
273
87
  }
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
88
+ const bridge = this.getAutomationBridge();
89
+ const response = await bridge.sendAutomationRequest('control_actor', {
90
+ action: 'delete',
91
+ actorNames: names
92
+ });
93
+ const result = (response?.data || response?.result || response) ?? {};
94
+ const deleted = result.deleted ?? names;
95
+ const missing = result.missing ?? [];
96
+ const errorObj = response?.error;
97
+ const errorCode = (typeof errorObj === 'object' ? errorObj.code : String(errorObj || result.error || '')).toUpperCase();
98
+ if (response && response.success === false && errorCode === 'DELETE_PARTIAL') {
99
+ return {
100
+ success: true,
101
+ message: errorObj?.message || response.message || 'Some actors could not be deleted',
102
+ deleted,
103
+ missing,
104
+ partial: true
105
+ };
280
106
  }
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`);
107
+ if (!response || response.success === false) {
108
+ throw new Error(errorObj?.message || response?.message || 'Failed to delete actors');
286
109
  }
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
110
  return {
304
111
  success: true,
305
- message: `Actor spawn attempted via console: ${spawnClass} at ${locX},${locY},${locZ}`,
306
- note: 'Console spawn result uncertain - verify in editor'
112
+ message: response.message || 'Deleted actors',
113
+ deleted: result.deleted || deleted,
114
+ ...result
307
115
  };
308
116
  }
309
- catch (err) {
310
- throw new Error(`Failed to spawn actor: ${err}`);
117
+ if (!params.actorName || typeof params.actorName !== 'string') {
118
+ throw new Error('Invalid actorName');
311
119
  }
120
+ return this.sendRequest('delete', { actorName: params.actorName }, 'control_actor');
312
121
  }
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();
122
+ async applyForce(params) {
123
+ if (!params.actorName || typeof params.actorName !== 'string') {
124
+ throw new Error('Invalid actorName');
125
+ }
126
+ if (!params.force || typeof params.force !== 'object') {
127
+ throw new Error('Invalid force vector');
128
+ }
129
+ const [forceX, forceY, forceZ] = ensureVector3(params.force, 'force vector');
130
+ if (forceX === 0 && forceY === 0 && forceZ === 0) {
131
+ return {
132
+ success: true,
133
+ message: `Zero force provided for ${params.actorName}; no-op`,
134
+ physicsEnabled: false,
135
+ noOp: true
136
+ };
137
+ }
138
+ return this.sendRequest('apply_force', {
139
+ actorName: params.actorName,
140
+ force: { x: forceX, y: forceY, z: forceZ }
141
+ }, 'control_actor');
327
142
  }
328
143
  resolveActorClass(classPath) {
329
- // Map common names to full Unreal class paths
330
144
  const classMap = {
331
145
  'PointLight': '/Script/Engine.PointLight',
332
146
  'DirectionalLight': '/Script/Engine.DirectionalLight',
@@ -349,55 +163,280 @@ def spawn_actor_from_class(actor_class, location, rotation):
349
163
  'AtmosphericFog': '/Script/Engine.AtmosphericFog',
350
164
  'SphereReflectionCapture': '/Script/Engine.SphereReflectionCapture',
351
165
  'BoxReflectionCapture': '/Script/Engine.BoxReflectionCapture',
352
- // PlaneReflectionCapture is abstract and cannot be spawned
353
166
  'DecalActor': '/Script/Engine.DecalActor'
354
167
  };
355
- // Check if it's a simple name that needs mapping
356
168
  if (classMap[classPath]) {
357
169
  return classMap[classPath];
358
170
  }
359
- // Check if it already looks like a full path
360
171
  if (classPath.startsWith('/Script/') || classPath.startsWith('/Game/')) {
361
172
  return classPath;
362
173
  }
363
174
  if (classPath.startsWith('/Engine/')) {
364
175
  return classPath;
365
176
  }
366
- // Check for Blueprint paths
367
177
  if (classPath.includes('Blueprint') || classPath.includes('BP_')) {
368
- // Ensure it has the proper prefix
369
178
  if (!classPath.startsWith('/Game/')) {
370
179
  return '/Game/' + classPath;
371
180
  }
372
181
  return classPath;
373
182
  }
374
- // Default: assume it's an engine class
375
183
  return '/Script/Engine.' + classPath;
376
184
  }
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;
185
+ async spawnBlueprint(params) {
186
+ const blueprintPath = typeof params.blueprintPath === 'string' ? params.blueprintPath.trim() : '';
187
+ if (!blueprintPath) {
188
+ throw new Error('Invalid blueprintPath');
189
+ }
190
+ const actorName = typeof params.actorName === 'string' && params.actorName.trim().length > 0 ? params.actorName.trim() : undefined;
191
+ const location = params.location ? ensureVector3(params.location, 'spawn_blueprint location') : undefined;
192
+ const rotation = params.rotation ? ensureRotation(params.rotation, 'spawn_blueprint rotation') : undefined;
193
+ const payload = { blueprintPath };
194
+ if (actorName)
195
+ payload.actorName = actorName;
196
+ if (location)
197
+ payload.location = { x: location[0], y: location[1], z: location[2] };
198
+ if (rotation)
199
+ payload.rotation = { pitch: rotation[0], yaw: rotation[1], roll: rotation[2] };
200
+ return this.sendRequest('spawn_blueprint', payload, 'control_actor');
201
+ }
202
+ async setTransform(params) {
203
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
204
+ if (!actorName) {
205
+ throw new Error('Invalid actorName');
206
+ }
207
+ const payload = { actorName };
208
+ if (params.location) {
209
+ const loc = ensureVector3(params.location, 'set_transform location');
210
+ payload.location = { x: loc[0], y: loc[1], z: loc[2] };
211
+ }
212
+ if (params.rotation) {
213
+ const rot = ensureRotation(params.rotation, 'set_transform rotation');
214
+ payload.rotation = { pitch: rot[0], yaw: rot[1], roll: rot[2] };
215
+ }
216
+ if (params.scale) {
217
+ const scl = ensureVector3(params.scale, 'set_transform scale');
218
+ payload.scale = { x: scl[0], y: scl[1], z: scl[2] };
219
+ }
220
+ return this.sendRequest('set_transform', payload, 'control_actor');
221
+ }
222
+ async getTransform(actorName) {
223
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
224
+ throw new Error('Invalid actorName');
225
+ }
226
+ return this.sendRequest('get_transform', { actorName }, 'control_actor')
227
+ .then(response => {
228
+ return response;
229
+ });
230
+ }
231
+ async setVisibility(params) {
232
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
233
+ if (!actorName) {
234
+ throw new Error('Invalid actorName');
235
+ }
236
+ return this.sendRequest('set_visibility', { actorName, visible: Boolean(params.visible) }, 'control_actor');
237
+ }
238
+ async addComponent(params) {
239
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
240
+ const componentType = typeof params.componentType === 'string' ? params.componentType.trim() : '';
241
+ if (!actorName)
242
+ throw new Error('Invalid actorName');
243
+ if (!componentType)
244
+ throw new Error('Invalid componentType');
245
+ return this.sendRequest('add_component', {
246
+ actorName,
247
+ componentType,
248
+ componentName: typeof params.componentName === 'string' ? params.componentName : undefined,
249
+ properties: params.properties
250
+ }, 'control_actor');
251
+ }
252
+ async setComponentProperties(params) {
253
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
254
+ const componentName = typeof params.componentName === 'string' ? params.componentName.trim() : '';
255
+ if (!actorName)
256
+ throw new Error('Invalid actorName');
257
+ if (!componentName)
258
+ throw new Error('Invalid componentName');
259
+ return this.sendRequest('set_component_properties', {
260
+ actorName,
261
+ componentName,
262
+ properties: params.properties ?? {}
263
+ }, 'control_actor');
264
+ }
265
+ async getComponents(actorName) {
266
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
267
+ throw new Error('Invalid actorName');
268
+ }
269
+ const response = await this.sendRequest('get_components', { actorName }, 'control_actor');
270
+ if (!response.success) {
271
+ return { success: false, error: response.error || `Failed to get components for actor ${actorName}` };
272
+ }
273
+ const data = response.data ?? response.result ?? response;
274
+ const components = Array.isArray(data)
275
+ ? data
276
+ : (Array.isArray(data?.components) ? data.components : []);
277
+ const count = typeof data?.count === 'number' ? data.count : components.length;
278
+ return {
279
+ success: true,
280
+ message: 'Actor components retrieved',
281
+ components,
282
+ count
283
+ };
284
+ }
285
+ async duplicate(params) {
286
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
287
+ if (!actorName)
288
+ throw new Error('Invalid actorName');
289
+ const payload = { actorName };
290
+ if (typeof params.newName === 'string' && params.newName.trim().length > 0) {
291
+ payload.newName = params.newName.trim();
292
+ }
293
+ if (params.offset) {
294
+ const offs = ensureVector3(params.offset, 'duplicate offset');
295
+ payload.offset = { x: offs[0], y: offs[1], z: offs[2] };
296
+ }
297
+ return this.sendRequest('duplicate', payload, 'control_actor');
298
+ }
299
+ async addTag(params) {
300
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
301
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
302
+ if (!actorName)
303
+ throw new Error('Invalid actorName');
304
+ if (!tag)
305
+ throw new Error('Invalid tag');
306
+ return this.sendRequest('add_tag', { actorName, tag }, 'control_actor');
307
+ }
308
+ async removeTag(params) {
309
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
310
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
311
+ if (!actorName)
312
+ throw new Error('Invalid actorName');
313
+ if (!tag)
314
+ throw new Error('Invalid tag');
315
+ return this.sendRequest('remove_tag', { actorName, tag }, 'control_actor');
316
+ }
317
+ async findByTag(params) {
318
+ const tag = typeof params.tag === 'string' ? params.tag.trim() : '';
319
+ if (!tag) {
320
+ return {
321
+ success: true,
322
+ message: 'Empty tag query; no actors matched',
323
+ data: {
324
+ actors: [],
325
+ count: 0
326
+ }
327
+ };
328
+ }
329
+ return this.sendRequest('find_by_tag', {
330
+ tag,
331
+ matchType: typeof params.matchType === 'string' ? params.matchType : undefined
332
+ }, 'control_actor');
333
+ }
334
+ async findByName(name) {
335
+ if (typeof name !== 'string' || name.trim().length === 0) {
336
+ throw new Error('Invalid actor name query');
337
+ }
338
+ return this.sendRequest('find_by_name', { name: name.trim() }, 'control_actor');
339
+ }
340
+ async detach(actorName) {
341
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
342
+ throw new Error('Invalid actorName');
343
+ }
344
+ return this.sendRequest('detach', { actorName }, 'control_actor');
345
+ }
346
+ async attach(params) {
347
+ const child = typeof params.childActor === 'string' ? params.childActor.trim() : '';
348
+ const parent = typeof params.parentActor === 'string' ? params.parentActor.trim() : '';
349
+ if (!child)
350
+ throw new Error('Invalid childActor');
351
+ if (!parent)
352
+ throw new Error('Invalid parentActor');
353
+ return this.sendRequest('attach', { childActor: child, parentActor: parent }, 'control_actor');
354
+ }
355
+ async deleteByTag(tag) {
356
+ if (typeof tag !== 'string' || tag.trim().length === 0) {
357
+ throw new Error('Invalid tag');
358
+ }
359
+ return this.sendRequest('delete_by_tag', { tag: tag.trim() }, 'control_actor');
360
+ }
361
+ async setBlueprintVariables(params) {
362
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
363
+ if (!actorName)
364
+ throw new Error('Invalid actorName');
365
+ return this.sendRequest('set_blueprint_variables', { actorName, variables: params.variables ?? {} }, 'control_actor');
366
+ }
367
+ async createSnapshot(params) {
368
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
369
+ const snapshotName = typeof params.snapshotName === 'string' ? params.snapshotName.trim() : '';
370
+ if (!actorName)
371
+ throw new Error('Invalid actorName');
372
+ if (!snapshotName)
373
+ throw new Error('Invalid snapshotName');
374
+ return this.sendRequest('create_snapshot', { actorName, snapshotName }, 'control_actor');
375
+ }
376
+ async restoreSnapshot(params) {
377
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
378
+ const snapshotName = typeof params.snapshotName === 'string' ? params.snapshotName.trim() : '';
379
+ if (!actorName)
380
+ throw new Error('Invalid actorName');
381
+ if (!snapshotName)
382
+ throw new Error('Invalid snapshotName');
383
+ return this.sendRequest('restore_snapshot', { actorName, snapshotName }, 'control_actor');
384
+ }
385
+ async exportActor(params) {
386
+ const actorName = typeof params.actorName === 'string' ? params.actorName.trim() : '';
387
+ if (!actorName)
388
+ throw new Error('Invalid actorName');
389
+ return this.sendRequest('export', {
390
+ actorName,
391
+ destinationPath: params.destinationPath
392
+ }, 'control_actor');
393
+ }
394
+ async getBoundingBox(actorName) {
395
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
396
+ throw new Error('Invalid actorName');
397
+ }
398
+ const response = await this.sendRequest('get_bounding_box', { actorName }, 'control_actor');
399
+ if (!response.success) {
400
+ return { success: false, error: response.error || `Failed to get bounding box for actor ${actorName}` };
401
+ }
402
+ return {
403
+ success: true,
404
+ message: 'Bounding box retrieved',
405
+ boundingBox: response.data || response.result || {}
406
+ };
407
+ }
408
+ async getMetadata(actorName) {
409
+ if (typeof actorName !== 'string' || actorName.trim().length === 0) {
410
+ throw new Error('Invalid actorName');
411
+ }
412
+ const response = await this.sendRequest('get_metadata', { actorName }, 'control_actor');
413
+ if (!response.success) {
414
+ return { success: false, error: response.error || `Failed to get metadata for actor ${actorName}` };
415
+ }
416
+ return {
417
+ success: true,
418
+ message: 'Actor metadata retrieved',
419
+ metadata: response.data || response.result || {}
420
+ };
421
+ }
422
+ async listActors(params) {
423
+ const payload = {};
424
+ if (params?.filter) {
425
+ payload.filter = params.filter;
426
+ }
427
+ const response = await this.sendRequest('list_actors', payload, 'control_actor');
428
+ if (!response.success) {
429
+ return { success: false, error: response.error || 'Failed to list actors' };
430
+ }
431
+ const dataObj = response.data || response.result || {};
432
+ const actorsRaw = response.actors || (dataObj && dataObj.actors) || (Array.isArray(dataObj) ? dataObj : []);
433
+ const actors = Array.isArray(actorsRaw) ? actorsRaw : [];
434
+ return {
435
+ success: true,
436
+ message: `Found ${actors.length} actors`,
437
+ actors,
438
+ count: actors.length
439
+ };
401
440
  }
402
441
  }
403
442
  //# sourceMappingURL=actors.js.map