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,762 +1,349 @@
1
- // Audio tools for Unreal Engine
2
- import JSON5 from 'json5';
3
- import { escapePythonString } from '../utils/python.js';
4
1
  export class AudioTools {
5
2
  bridge;
6
- constructor(bridge) {
3
+ automationBridge;
4
+ constructor(bridge, automationBridge) {
7
5
  this.bridge = bridge;
6
+ this.automationBridge = automationBridge;
8
7
  }
9
- interpretResult(resp, defaults) {
10
- const normalizePayload = (payload) => {
11
- const warningsValue = payload?.warnings;
12
- const warningsText = Array.isArray(warningsValue)
13
- ? (warningsValue.length > 0 ? warningsValue.join('; ') : undefined)
14
- : typeof warningsValue === 'string' && warningsValue.trim() !== ''
15
- ? warningsValue
16
- : undefined;
17
- if (payload?.success === true) {
18
- const message = typeof payload?.message === 'string' && payload.message.trim() !== ''
19
- ? payload.message
20
- : defaults.successMessage;
21
- return {
22
- success: true,
23
- message,
24
- details: warningsText
25
- };
26
- }
27
- const error = (typeof payload?.error === 'string' && payload.error.trim() !== ''
28
- ? payload.error
29
- : undefined)
30
- ?? (typeof payload?.message === 'string' && payload.message.trim() !== ''
31
- ? payload.message
32
- : undefined)
33
- ?? defaults.failureMessage;
34
- return {
35
- success: false,
36
- error,
37
- details: warningsText
38
- };
39
- };
40
- if (resp && typeof resp === 'object') {
41
- const payload = resp;
42
- if ('success' in payload || 'error' in payload || 'message' in payload || 'warnings' in payload) {
43
- return normalizePayload(payload);
44
- }
45
- }
46
- const raw = typeof resp === 'string' ? resp : JSON.stringify(resp);
47
- const extractJson = (input) => {
48
- const marker = 'RESULT:';
49
- const markerIndex = input.lastIndexOf(marker);
50
- if (markerIndex === -1) {
51
- return undefined;
52
- }
53
- const afterMarker = input.slice(markerIndex + marker.length);
54
- const firstBraceIndex = afterMarker.indexOf('{');
55
- if (firstBraceIndex === -1) {
56
- return undefined;
57
- }
58
- let depth = 0;
59
- let inString = false;
60
- let escapeNext = false;
61
- for (let i = firstBraceIndex; i < afterMarker.length; i++) {
62
- const char = afterMarker[i];
63
- if (inString) {
64
- if (escapeNext) {
65
- escapeNext = false;
66
- continue;
67
- }
68
- if (char === '\\') {
69
- escapeNext = true;
70
- continue;
71
- }
72
- if (char === '"') {
73
- inString = false;
74
- }
75
- continue;
76
- }
77
- if (char === '"') {
78
- inString = true;
79
- continue;
80
- }
81
- if (char === '{') {
82
- depth += 1;
83
- }
84
- else if (char === '}') {
85
- depth -= 1;
86
- if (depth === 0) {
87
- return afterMarker.slice(firstBraceIndex, i + 1);
88
- }
89
- }
90
- }
91
- const fallbackMatch = /\{[\s\S]*\}/.exec(afterMarker);
92
- return fallbackMatch ? fallbackMatch[0] : undefined;
8
+ setAutomationBridge(automationBridge) { this.automationBridge = automationBridge; }
9
+ validateAudioParams(volume, pitch) {
10
+ const v = volume ?? 1.0;
11
+ const p = pitch ?? 1.0;
12
+ return {
13
+ volume: Math.max(0.0, Math.min(v, 4.0)),
14
+ pitch: Math.max(0.01, Math.min(p, 4.0))
93
15
  };
94
- const jsonPayload = extractJson(raw);
95
- if (jsonPayload) {
96
- const parseAttempts = [
97
- {
98
- label: 'json',
99
- parser: () => JSON.parse(jsonPayload)
100
- },
101
- {
102
- label: 'json5',
103
- parser: () => JSON5.parse(jsonPayload)
104
- }
105
- ];
106
- const sanitizedForJson5 = jsonPayload
107
- .replace(/\bTrue\b/g, 'true')
108
- .replace(/\bFalse\b/g, 'false')
109
- .replace(/\bNone\b/g, 'null');
110
- if (sanitizedForJson5 !== jsonPayload) {
111
- parseAttempts.push({ label: 'json5-sanitized', parser: () => JSON5.parse(sanitizedForJson5) });
112
- }
113
- const parseErrors = [];
114
- for (const attempt of parseAttempts) {
115
- try {
116
- const parsed = attempt.parser();
117
- if (parsed && typeof parsed === 'object') {
118
- return normalizePayload(parsed);
119
- }
120
- }
121
- catch (err) {
122
- parseErrors.push(`${attempt.label}: ${err instanceof Error ? err.message : String(err)}`);
123
- }
124
- }
125
- const errorMatch = /["']error["']\s*:\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)')/i.exec(jsonPayload);
126
- const messageMatch = /["']message["']\s*:\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)')/i.exec(jsonPayload);
127
- const fallbackText = errorMatch?.[1] ?? errorMatch?.[2] ?? messageMatch?.[1] ?? messageMatch?.[2];
128
- const errorText = fallbackText && fallbackText.trim().length > 0
129
- ? fallbackText.trim()
130
- : `${defaults.failureMessage}: ${parseErrors[0] ?? 'Unable to parse RESULT payload'}`;
131
- const snippet = jsonPayload.length > 240 ? `${jsonPayload.slice(0, 240)}…` : jsonPayload;
132
- const detailsParts = [];
133
- if (parseErrors.length > 0) {
134
- detailsParts.push(`Parse attempts failed: ${parseErrors.join('; ')}`);
135
- }
136
- detailsParts.push(`Raw payload: ${snippet}`);
137
- const detailsText = detailsParts.join(' | ');
138
- return {
139
- success: false,
140
- error: errorText,
141
- details: detailsText
142
- };
143
- }
144
- return { success: false, error: defaults.failureMessage };
145
16
  }
146
- // Create sound cue
147
17
  async createSoundCue(params) {
148
- const escapePyString = (value) => value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
149
- const toPyNumber = (value) => value === undefined || value === null || !Number.isFinite(value) ? 'None' : String(value);
150
- const toPyBool = (value) => value === undefined || value === null ? 'None' : value ? 'True' : 'False';
18
+ if (!this.automationBridge) {
19
+ throw new Error('Automation Bridge not available. Audio operations require plugin support.');
20
+ }
151
21
  const path = params.savePath || '/Game/Audio/Cues';
152
- const wavePath = params.wavePath || '';
153
- const attenuationPath = params.settings?.attenuationSettings || '';
154
- const volumeLiteral = toPyNumber(params.settings?.volume);
155
- const pitchLiteral = toPyNumber(params.settings?.pitch);
156
- const loopingLiteral = toPyBool(params.settings?.looping);
157
- const py = `
158
- import unreal
159
- import json
160
-
161
- name = r"${escapePyString(params.name)}"
162
- package_path = r"${escapePyString(path)}"
163
- wave_path = r"${escapePyString(wavePath)}"
164
- attenuation_path = r"${escapePyString(attenuationPath)}"
165
- attach_wave = ${params.wavePath ? 'True' : 'False'}
166
- volume_override = ${volumeLiteral}
167
- pitch_override = ${pitchLiteral}
168
- looping_override = ${loopingLiteral}
169
-
170
- result = {
171
- "success": False,
172
- "message": "",
173
- "error": "",
174
- "warnings": []
175
- }
176
-
177
- try:
178
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
179
- if not asset_tools:
180
- result["error"] = "AssetToolsHelpers unavailable"
181
- raise SystemExit(0)
182
-
183
- factory = None
184
- try:
185
- factory = unreal.SoundCueFactoryNew()
186
- except Exception:
187
- factory = None
188
-
189
- if not factory:
190
- result["error"] = "SoundCueFactoryNew unavailable"
191
- raise SystemExit(0)
192
-
193
- package_path = package_path.rstrip('/') if package_path else package_path
194
-
195
- asset = asset_tools.create_asset(
196
- asset_name=name,
197
- package_path=package_path,
198
- asset_class=unreal.SoundCue,
199
- factory=factory
200
- )
201
-
202
- if not asset:
203
- result["error"] = "Failed to create SoundCue"
204
- raise SystemExit(0)
205
-
206
- asset_subsystem = None
207
- try:
208
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
209
- except Exception:
210
- asset_subsystem = None
211
-
212
- editor_library = unreal.EditorAssetLibrary
213
-
214
- if attach_wave:
215
- wave_exists = False
216
- try:
217
- if asset_subsystem and hasattr(asset_subsystem, "does_asset_exist"):
218
- wave_exists = asset_subsystem.does_asset_exist(wave_path)
219
- else:
220
- wave_exists = editor_library.does_asset_exist(wave_path)
221
- except Exception as existence_error:
222
- result["warnings"].append(f"Wave lookup failed: {existence_error}")
223
-
224
- if not wave_exists:
225
- result["warnings"].append(f"Wave asset not found: {wave_path}")
226
- else:
227
- try:
228
- if asset_subsystem and hasattr(asset_subsystem, "load_asset"):
229
- wave_asset = asset_subsystem.load_asset(wave_path)
230
- else:
231
- wave_asset = editor_library.load_asset(wave_path)
232
- if wave_asset:
233
- # Hooking up cue nodes via Python is non-trivial; surface warning for manual setup
234
- result["warnings"].append("Sound cue created without automatic wave node hookup")
235
- except Exception as wave_error:
236
- result["warnings"].append(f"Failed to load wave asset: {wave_error}")
237
-
238
- if volume_override is not None and hasattr(asset, "volume_multiplier"):
239
- asset.volume_multiplier = volume_override
240
- if pitch_override is not None and hasattr(asset, "pitch_multiplier"):
241
- asset.pitch_multiplier = pitch_override
242
- if looping_override is not None and hasattr(asset, "b_looping"):
243
- asset.b_looping = looping_override
244
-
245
- if attenuation_path:
246
- try:
247
- attenuation_asset = editor_library.load_asset(attenuation_path)
248
- if attenuation_asset:
249
- applied = False
250
- if hasattr(asset, "set_attenuation_settings"):
251
- try:
252
- asset.set_attenuation_settings(attenuation_asset)
253
- applied = True
254
- except Exception:
255
- applied = False
256
- if not applied and hasattr(asset, "attenuation_settings"):
257
- asset.attenuation_settings = attenuation_asset
258
- applied = True
259
- if not applied:
260
- result["warnings"].append("Attenuation asset loaded but could not be applied automatically")
261
- except Exception as attenuation_error:
262
- result["warnings"].append(f"Failed to apply attenuation: {attenuation_error}")
263
-
264
- try:
265
- save_target = f"{package_path}/{name}" if package_path else name
266
- if asset_subsystem and hasattr(asset_subsystem, "save_asset"):
267
- asset_subsystem.save_asset(save_target)
268
- else:
269
- editor_library.save_asset(save_target)
270
- except Exception as save_error:
271
- result["warnings"].append(f"Save failed: {save_error}")
272
-
273
- result["success"] = True
274
- result["message"] = "Sound cue created"
275
-
276
- except SystemExit:
277
- pass
278
- except Exception as error:
279
- result["error"] = str(error)
280
-
281
- finally:
282
- payload = dict(result)
283
- if payload.get("success"):
284
- if not payload.get("message"):
285
- payload["message"] = "Sound cue created"
286
- payload.pop("error", None)
287
- else:
288
- if not payload.get("error"):
289
- payload["error"] = payload.get("message") or "Failed to create SoundCue"
290
- if not payload.get("message"):
291
- payload["message"] = payload["error"]
292
- if not payload.get("warnings"):
293
- payload.pop("warnings", None)
294
- print('RESULT:' + json.dumps(payload))
295
- `.trim();
22
+ const { volume, pitch } = this.validateAudioParams(params.settings?.volume, params.settings?.pitch);
296
23
  try {
297
- const resp = await this.bridge.executePython(py);
298
- return this.interpretResult(resp, {
299
- successMessage: 'Sound cue created',
300
- failureMessage: 'Failed to create SoundCue'
24
+ const response = await this.automationBridge.sendAutomationRequest('create_sound_cue', {
25
+ name: params.name,
26
+ packagePath: path,
27
+ wavePath: params.wavePath,
28
+ attenuationPath: params.settings?.attenuationSettings,
29
+ volume,
30
+ pitch,
31
+ looping: params.settings?.looping
32
+ }, {
33
+ timeoutMs: 60000
301
34
  });
35
+ if (response.success === false) {
36
+ return { success: false, error: response.error || response.message || 'Failed to create SoundCue' };
37
+ }
38
+ return {
39
+ success: true,
40
+ message: response.message || 'Sound cue created',
41
+ ...(response.result || {})
42
+ };
302
43
  }
303
- catch (e) {
304
- return { success: false, error: `Failed to create sound cue: ${e}` };
44
+ catch (error) {
45
+ return { success: false, error: `Failed to create sound cue: ${error instanceof Error ? error.message : String(error)}` };
305
46
  }
306
47
  }
307
- // Play sound at location
308
48
  async playSoundAtLocation(params) {
309
- const volume = params.volume ?? 1.0;
310
- const pitch = params.pitch ?? 1.0;
311
- const startTime = params.startTime ?? 0.0;
312
- const soundPath = params.soundPath ?? '';
313
- const py = `
314
- import unreal
315
- import json
316
-
317
- result = {
318
- "success": False,
319
- "message": "",
320
- "error": "",
321
- "warnings": []
322
- }
323
-
324
- try:
325
- path = "${escapePythonString(soundPath)}"
326
- if not unreal.EditorAssetLibrary.does_asset_exist(path):
327
- result["error"] = "Sound asset not found"
328
- raise SystemExit(0)
329
-
330
- snd = unreal.EditorAssetLibrary.load_asset(path)
331
- if not snd:
332
- result["error"] = f"Failed to load sound asset: {path}"
333
- raise SystemExit(0)
334
-
335
- world = None
336
- try:
337
- world = unreal.EditorUtilityLibrary.get_editor_world()
338
- except Exception:
339
- world = None
340
-
341
- if not world:
342
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
343
- if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world'):
344
- world = editor_subsystem.get_editor_world()
345
-
346
- if not world:
347
- try:
348
- world = unreal.EditorSubsystemLibrary.get_editor_world()
349
- except Exception:
350
- world = None
351
-
352
- if not world:
353
- result["error"] = "Unable to resolve editor world. Start PIE and ensure Editor Scripting Utilities is enabled."
354
- raise SystemExit(0)
355
-
356
- loc = unreal.Vector(${params.location[0]}, ${params.location[1]}, ${params.location[2]})
357
- rot = unreal.Rotator(0.0, 0.0, 0.0)
358
- unreal.GameplayStatics.spawn_sound_at_location(world, snd, loc, rot, ${volume}, ${pitch}, ${startTime})
359
-
360
- result["success"] = True
361
- result["message"] = "Sound played"
362
-
363
- except SystemExit:
364
- pass
365
- except Exception as e:
366
- result["error"] = str(e)
367
- finally:
368
- payload = dict(result)
369
- if payload.get("success"):
370
- if not payload.get("message"):
371
- payload["message"] = "Sound played"
372
- payload.pop("error", None)
373
- else:
374
- if not payload.get("error"):
375
- payload["error"] = payload.get("message") or "Failed to play sound"
376
- if not payload.get("message"):
377
- payload["message"] = payload["error"]
378
- if not payload.get("warnings"):
379
- payload.pop("warnings", None)
380
- print('RESULT:' + json.dumps(payload))
381
- `.trim();
49
+ if (!this.automationBridge) {
50
+ throw new Error('Automation Bridge not available. Audio operations require plugin support.');
51
+ }
52
+ const { volume, pitch } = this.validateAudioParams(params.volume, params.pitch);
382
53
  try {
383
- const resp = await this.bridge.executePythonWithResult(py);
384
- return this.interpretResult(resp, {
385
- successMessage: 'Sound played',
386
- failureMessage: 'Failed to play sound'
54
+ const response = await this.automationBridge.sendAutomationRequest('play_sound_at_location', {
55
+ soundPath: params.soundPath,
56
+ location: params.location,
57
+ rotation: params.rotation ?? [0, 0, 0],
58
+ volume,
59
+ pitch,
60
+ startTime: params.startTime ?? 0.0,
61
+ attenuationPath: params.attenuationPath,
62
+ concurrencyPath: params.concurrencyPath
63
+ }, {
64
+ timeoutMs: 30000
387
65
  });
66
+ if (response.success === false) {
67
+ return { success: false, error: response.error || response.message || 'Failed to play sound' };
68
+ }
69
+ return {
70
+ success: true,
71
+ message: response.message || 'Sound played',
72
+ ...(response.result || {})
73
+ };
388
74
  }
389
- catch (e) {
390
- return { success: false, error: `Failed to play sound: ${e}` };
75
+ catch (error) {
76
+ return { success: false, error: `Failed to play sound: ${error instanceof Error ? error.message : String(error)}` };
391
77
  }
392
78
  }
393
- // Play sound 2D
394
79
  async playSound2D(params) {
395
- const volume = params.volume ?? 1.0;
396
- const pitch = params.pitch ?? 1.0;
397
- const startTime = params.startTime ?? 0.0;
398
- const soundPath = params.soundPath ?? '';
399
- const py = `
400
- import unreal
401
- import json
402
-
403
- result = {
404
- "success": False,
405
- "message": "",
406
- "error": "",
407
- "warnings": []
408
- }
409
-
410
- try:
411
- path = "${escapePythonString(soundPath)}"
412
- if not unreal.EditorAssetLibrary.does_asset_exist(path):
413
- result["error"] = "Sound asset not found"
414
- raise SystemExit(0)
415
-
416
- snd = unreal.EditorAssetLibrary.load_asset(path)
417
- if not snd:
418
- result["error"] = f"Failed to load sound asset: {path}"
419
- raise SystemExit(0)
420
-
421
- world = None
422
- try:
423
- world = unreal.EditorUtilityLibrary.get_editor_world()
424
- except Exception:
425
- world = None
426
-
427
- if not world:
428
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
429
- if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world'):
430
- world = editor_subsystem.get_editor_world()
431
-
432
- if not world:
433
- try:
434
- world = unreal.EditorSubsystemLibrary.get_editor_world()
435
- except Exception:
436
- world = None
437
-
438
- if not world:
439
- result["error"] = "Unable to resolve editor world. Start PIE and ensure Editor Scripting Utilities is enabled."
440
- raise SystemExit(0)
441
-
442
- ok = False
443
- try:
444
- unreal.GameplayStatics.spawn_sound_2d(world, snd, ${volume}, ${pitch}, ${startTime})
445
- ok = True
446
- except AttributeError:
447
- try:
448
- unreal.GameplayStatics.play_sound_2d(world, snd, ${volume}, ${pitch}, ${startTime})
449
- ok = True
450
- except AttributeError:
451
- pass
452
-
453
- if not ok:
454
- cam_loc = unreal.Vector(0.0, 0.0, 0.0)
455
- try:
456
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
457
- if editor_subsystem and hasattr(editor_subsystem, 'get_level_viewport_camera_info'):
458
- info = editor_subsystem.get_level_viewport_camera_info()
459
- if isinstance(info, (list, tuple)) and len(info) > 0:
460
- cam_loc = info[0]
461
- except Exception:
462
- try:
463
- controller = world.get_first_player_controller()
464
- if controller:
465
- pawn = controller.get_pawn()
466
- if pawn:
467
- cam_loc = pawn.get_actor_location()
468
- except Exception:
469
- pass
470
-
471
- try:
472
- rot = unreal.Rotator(0.0, 0.0, 0.0)
473
- unreal.GameplayStatics.spawn_sound_at_location(world, snd, cam_loc, rot, ${volume}, ${pitch}, ${startTime})
474
- ok = True
475
- result["warnings"].append("Fell back to 3D playback at camera location")
476
- except Exception as location_error:
477
- result["warnings"].append(f"Failed fallback playback: {location_error}")
478
-
479
- if not ok:
480
- result["error"] = "Failed to play sound in 2D or fallback configuration"
481
- raise SystemExit(0)
482
-
483
- result["success"] = True
484
- result["message"] = "Sound2D played"
485
-
486
- except SystemExit:
487
- pass
488
- except Exception as e:
489
- result["error"] = str(e)
490
- finally:
491
- payload = dict(result)
492
- if payload.get("success"):
493
- if not payload.get("message"):
494
- payload["message"] = "Sound2D played"
495
- payload.pop("error", None)
496
- else:
497
- if not payload.get("error"):
498
- payload["error"] = payload.get("message") or "Failed to play sound2D"
499
- if not payload.get("message"):
500
- payload["message"] = payload["error"]
501
- if not payload.get("warnings"):
502
- payload.pop("warnings", None)
503
- print('RESULT:' + json.dumps(payload))
504
- `.trim();
80
+ if (!this.automationBridge) {
81
+ throw new Error('Automation Bridge not available. Audio operations require plugin support.');
82
+ }
505
83
  try {
506
- const resp = await this.bridge.executePythonWithResult(py);
507
- return this.interpretResult(resp, {
508
- successMessage: 'Sound2D played',
509
- failureMessage: 'Failed to play sound2D'
84
+ const response = await this.automationBridge.sendAutomationRequest('play_sound_2d', {
85
+ soundPath: params.soundPath,
86
+ volume: params.volume ?? 1.0,
87
+ pitch: params.pitch ?? 1.0,
88
+ startTime: params.startTime ?? 0.0
89
+ }, {
90
+ timeoutMs: 30000
510
91
  });
92
+ if (response.success === false) {
93
+ return { success: false, error: response.error || response.message || 'Failed to play 2D sound' };
94
+ }
95
+ return {
96
+ success: true,
97
+ message: response.message || '2D sound played',
98
+ ...(response.result || {})
99
+ };
511
100
  }
512
- catch (e) {
513
- return { success: false, error: `Failed to play sound2D: ${e}` };
101
+ catch (error) {
102
+ return { success: false, error: `Failed to play 2D sound: ${error instanceof Error ? error.message : String(error)}` };
514
103
  }
515
104
  }
516
- // Create audio component
517
- async createAudioComponent(params) {
518
- const commands = [];
519
- commands.push(`AddAudioComponent ${params.actorName} ${params.componentName} ${params.soundPath}`);
520
- if (params.autoPlay !== undefined) {
521
- commands.push(`SetAudioComponentAutoPlay ${params.actorName}.${params.componentName} ${params.autoPlay}`);
105
+ async playSound(soundPath, volume, pitch) {
106
+ return this.playSound2D({
107
+ soundPath,
108
+ volume,
109
+ pitch
110
+ });
111
+ }
112
+ async createAudioComponent(_params) {
113
+ if (!this.automationBridge) {
114
+ throw new Error('Automation Bridge not available. Creating audio components requires plugin support.');
522
115
  }
523
- if (params.is3D !== undefined) {
524
- commands.push(`SetAudioComponent3D ${params.actorName}.${params.componentName} ${params.is3D}`);
116
+ try {
117
+ const response = await this.automationBridge.sendAutomationRequest('create_audio_component', {
118
+ actorName: _params.actorName,
119
+ componentName: _params.componentName,
120
+ soundPath: _params.soundPath,
121
+ autoPlay: _params.autoPlay ?? false,
122
+ is3D: _params.is3D ?? true
123
+ });
124
+ return response.success
125
+ ? { success: true, message: response.message || 'Audio component created', ...(response.result || {}) }
126
+ : { success: false, error: response.error || response.message || 'Failed to create audio component' };
525
127
  }
526
- for (const cmd of commands) {
527
- await this.bridge.executeConsoleCommand(cmd);
128
+ catch (error) {
129
+ return { success: false, error: `Failed to create audio component: ${error instanceof Error ? error.message : String(error)}` };
528
130
  }
529
- return { success: true, message: `Audio component ${params.componentName} added to ${params.actorName}` };
530
131
  }
531
- // Set sound attenuation
532
- async setSoundAttenuation(params) {
533
- const commands = [];
534
- commands.push(`CreateAttenuationSettings ${params.name}`);
535
- if (params.innerRadius !== undefined) {
536
- commands.push(`SetAttenuationInnerRadius ${params.name} ${params.innerRadius}`);
132
+ async setSoundAttenuation(_params) {
133
+ if (!this.automationBridge) {
134
+ throw new Error('Automation Bridge not available. Setting sound attenuation requires plugin support.');
537
135
  }
538
- if (params.falloffDistance !== undefined) {
539
- commands.push(`SetAttenuationFalloffDistance ${params.name} ${params.falloffDistance}`);
136
+ try {
137
+ const response = await this.automationBridge.sendAutomationRequest('set_sound_attenuation', {
138
+ name: _params.name,
139
+ innerRadius: _params.innerRadius,
140
+ falloffDistance: _params.falloffDistance,
141
+ attenuationShape: _params.attenuationShape,
142
+ falloffMode: _params.falloffMode
143
+ });
144
+ return response.success
145
+ ? { success: true, message: response.message || 'Sound attenuation set', ...(response.result || {}) }
146
+ : { success: false, error: response.error || response.message || 'Failed to set sound attenuation' };
540
147
  }
541
- if (params.attenuationShape) {
542
- commands.push(`SetAttenuationShape ${params.name} ${params.attenuationShape}`);
148
+ catch (error) {
149
+ return { success: false, error: `Failed to set sound attenuation: ${error instanceof Error ? error.message : String(error)}` };
543
150
  }
544
- if (params.falloffMode) {
545
- commands.push(`SetAttenuationFalloffMode ${params.name} ${params.falloffMode}`);
151
+ }
152
+ async createSoundClass(_params) {
153
+ if (!this.automationBridge) {
154
+ throw new Error('Automation Bridge not available. Creating sound classes requires plugin support.');
546
155
  }
547
- for (const cmd of commands) {
548
- await this.bridge.executeConsoleCommand(cmd);
156
+ try {
157
+ const response = await this.automationBridge.sendAutomationRequest('create_sound_class', {
158
+ name: _params.name,
159
+ parentClass: _params.parentClass,
160
+ properties: _params.properties
161
+ });
162
+ return response.success
163
+ ? { success: true, message: response.message || 'Sound class created', ...(response.result || {}) }
164
+ : { success: false, error: response.error || response.message || 'Failed to create sound class' };
165
+ }
166
+ catch (error) {
167
+ return { success: false, error: `Failed to create sound class: ${error instanceof Error ? error.message : String(error)}` };
549
168
  }
550
- return { success: true, message: `Attenuation settings ${params.name} configured` };
551
169
  }
552
- // Create sound class
553
- async createSoundClass(params) {
554
- const commands = [];
555
- const parent = params.parentClass || 'Master';
556
- commands.push(`CreateSoundClass ${params.name} ${parent}`);
557
- if (params.properties) {
558
- if (params.properties.volume !== undefined) {
559
- commands.push(`SetSoundClassVolume ${params.name} ${params.properties.volume}`);
560
- }
561
- if (params.properties.pitch !== undefined) {
562
- commands.push(`SetSoundClassPitch ${params.name} ${params.properties.pitch}`);
563
- }
564
- if (params.properties.lowPassFilterFrequency !== undefined) {
565
- commands.push(`SetSoundClassLowPassFilter ${params.name} ${params.properties.lowPassFilterFrequency}`);
566
- }
567
- if (params.properties.attenuationDistanceScale !== undefined) {
568
- commands.push(`SetSoundClassAttenuationScale ${params.name} ${params.properties.attenuationDistanceScale}`);
569
- }
170
+ async createSoundMix(_params) {
171
+ if (!this.automationBridge) {
172
+ throw new Error('Automation Bridge not available. Creating sound mixes requires plugin support.');
570
173
  }
571
- for (const cmd of commands) {
572
- await this.bridge.executeConsoleCommand(cmd);
174
+ try {
175
+ const response = await this.automationBridge.sendAutomationRequest('create_sound_mix', {
176
+ name: _params.name,
177
+ classAdjusters: _params.classAdjusters
178
+ });
179
+ return response.success
180
+ ? { success: true, message: response.message || 'Sound mix created', ...(response.result || {}) }
181
+ : { success: false, error: response.error || response.message || 'Failed to create sound mix' };
182
+ }
183
+ catch (error) {
184
+ return { success: false, error: `Failed to create sound mix: ${error instanceof Error ? error.message : String(error)}` };
573
185
  }
574
- return { success: true, message: `Sound class ${params.name} created` };
575
186
  }
576
- // Create sound mix
577
- async createSoundMix(params) {
578
- const commands = [];
579
- commands.push(`CreateSoundMix ${params.name}`);
580
- if (params.classAdjusters) {
581
- for (const adjuster of params.classAdjusters) {
582
- commands.push(`AddSoundMixClassAdjuster ${params.name} ${adjuster.soundClass}`);
583
- if (adjuster.volumeAdjuster !== undefined) {
584
- commands.push(`SetSoundMixVolume ${params.name} ${adjuster.soundClass} ${adjuster.volumeAdjuster}`);
585
- }
586
- if (adjuster.pitchAdjuster !== undefined) {
587
- commands.push(`SetSoundMixPitch ${params.name} ${adjuster.soundClass} ${adjuster.pitchAdjuster}`);
588
- }
589
- if (adjuster.fadeInTime !== undefined) {
590
- commands.push(`SetSoundMixFadeIn ${params.name} ${adjuster.soundClass} ${adjuster.fadeInTime}`);
591
- }
592
- if (adjuster.fadeOutTime !== undefined) {
593
- commands.push(`SetSoundMixFadeOut ${params.name} ${adjuster.soundClass} ${adjuster.fadeOutTime}`);
594
- }
595
- }
187
+ async pushSoundMix(_params) {
188
+ if (!this.automationBridge) {
189
+ throw new Error('Automation Bridge not available. Pushing sound mixes requires plugin support.');
596
190
  }
597
- for (const cmd of commands) {
598
- await this.bridge.executeConsoleCommand(cmd);
191
+ try {
192
+ const response = await this.automationBridge.sendAutomationRequest('push_sound_mix', {
193
+ mixName: _params.mixName
194
+ });
195
+ return response.success
196
+ ? { success: true, message: response.message || 'Sound mix pushed', ...(response.result || {}) }
197
+ : { success: false, error: response.error || response.message || 'Failed to push sound mix' };
198
+ }
199
+ catch (error) {
200
+ return { success: false, error: `Failed to push sound mix: ${error instanceof Error ? error.message : String(error)}` };
599
201
  }
600
- return { success: true, message: `Sound mix ${params.name} created` };
601
- }
602
- // Push/Pop sound mix
603
- async pushSoundMix(params) {
604
- const command = `PushSoundMix ${params.mixName}`;
605
- return this.bridge.executeConsoleCommand(command);
606
202
  }
607
- async popSoundMix(params) {
608
- const command = `PopSoundMix ${params.mixName}`;
609
- return this.bridge.executeConsoleCommand(command);
203
+ async popSoundMix(_params) {
204
+ if (!this.automationBridge) {
205
+ throw new Error('Automation Bridge not available. Popping sound mixes requires plugin support.');
206
+ }
207
+ try {
208
+ const response = await this.automationBridge.sendAutomationRequest('pop_sound_mix', {
209
+ mixName: _params.mixName
210
+ });
211
+ return response.success
212
+ ? { success: true, message: response.message || 'Sound mix popped', ...(response.result || {}) }
213
+ : { success: false, error: response.error || response.message || 'Failed to pop sound mix' };
214
+ }
215
+ catch (error) {
216
+ return { success: false, error: `Failed to pop sound mix: ${error instanceof Error ? error.message : String(error)}` };
217
+ }
610
218
  }
611
- // Set master volume
612
219
  async setMasterVolume(params) {
613
- // Clamp volume between 0 and 1
614
220
  const vol = Math.max(0.0, Math.min(1.0, params.volume));
615
- // Use the proper Unreal Engine audio command
616
- // Note: au.Master.Volume is the correct console variable for master volume
617
221
  const command = `au.Master.Volume ${vol}`;
618
222
  try {
619
223
  await this.bridge.executeConsoleCommand(command);
620
224
  return { success: true, message: `Master volume set to ${vol}` };
621
225
  }
622
226
  catch (e) {
623
- // Fallback to Python method if console command fails
624
- const py = `
625
- import unreal
626
- import json
627
- try:
628
- # Try using AudioMixerBlueprintLibrary if available
629
- try:
630
- unreal.AudioMixerBlueprintLibrary.set_overall_volume_multiplier(${vol})
631
- print('RESULT:' + json.dumps({'success': True}))
632
- except AttributeError:
633
- # Fallback to GameplayStatics method using modern subsystems
634
- try:
635
- # Try modern subsystem first
636
- try:
637
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
638
- if hasattr(editor_subsystem, 'get_editor_world'):
639
- world = editor_subsystem.get_editor_world()
640
- else:
641
- world = unreal.EditorLevelLibrary.get_editor_world()
642
- except Exception:
643
- world = unreal.EditorLevelLibrary.get_editor_world()
644
- unreal.GameplayStatics.set_global_pitch_modulation(world, 1.0, 0.0) # Reset pitch
645
- unreal.GameplayStatics.set_global_time_dilation(world, 1.0) # Reset time
646
- # Note: There's no direct master volume in GameplayStatics, use sound class
647
- print('RESULT:' + json.dumps({'success': False, 'error': 'Master volume control not available, use sound classes instead'}))
648
- except Exception as e2:
649
- print('RESULT:' + json.dumps({'success': False, 'error': str(e2)}))
650
- except Exception as e:
651
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
652
- `.trim();
653
- try {
654
- const resp = await this.bridge.executePython(py);
655
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
656
- const m = out.match(/RESULT:({.*})/);
657
- if (m) {
658
- try {
659
- const parsed = JSON.parse(m[1]);
660
- return parsed.success
661
- ? { success: true, message: `Master volume set to ${vol}` }
662
- : { success: false, error: parsed.error };
663
- }
664
- catch { }
665
- }
666
- return { success: true, message: 'Master volume set command executed' };
667
- }
668
- catch {
669
- return { success: false, error: `Failed to set master volume: ${e}` };
670
- }
227
+ return { success: false, error: `Failed to set master volume: ${e instanceof Error ? e.message : String(e)}` };
671
228
  }
672
229
  }
673
- // Create ambient sound
674
- async createAmbientSound(params) {
675
- const commands = [];
676
- commands.push(`SpawnAmbientSound ${params.name} ${params.location.join(' ')} ${params.soundPath}`);
677
- if (params.volume !== undefined) {
678
- commands.push(`SetAmbientVolume ${params.name} ${params.volume}`);
679
- }
680
- if (params.radius !== undefined) {
681
- commands.push(`SetAmbientRadius ${params.name} ${params.radius}`);
230
+ async createAmbientSound(_params) {
231
+ if (!this.automationBridge) {
232
+ throw new Error('Automation Bridge not available. Creating ambient sounds requires plugin support.');
682
233
  }
683
- if (params.autoPlay !== undefined) {
684
- commands.push(`SetAmbientAutoPlay ${params.name} ${params.autoPlay}`);
234
+ try {
235
+ const response = await this.automationBridge.sendAutomationRequest('create_ambient_sound', {
236
+ soundPath: _params.soundPath,
237
+ location: _params.location,
238
+ volume: _params.volume ?? 1.0,
239
+ pitch: _params.pitch ?? 1.0,
240
+ startTime: _params.startTime ?? 0.0,
241
+ attenuationPath: _params.attenuationPath,
242
+ concurrencyPath: _params.concurrencyPath
243
+ });
244
+ return response.success
245
+ ? { success: true, message: response.message || 'Ambient sound created', ...(response.result || {}) }
246
+ : { success: false, error: response.error || response.message || 'Failed to create ambient sound' };
685
247
  }
686
- for (const cmd of commands) {
687
- await this.bridge.executeConsoleCommand(cmd);
248
+ catch (error) {
249
+ return { success: false, error: `Failed to create ambient sound: ${error instanceof Error ? error.message : String(error)}` };
688
250
  }
689
- return { success: true, message: `Ambient sound ${params.name} created` };
690
251
  }
691
- // Create reverb zone
692
- async createReverbZone(params) {
693
- const commands = [];
694
- commands.push(`CreateReverbVolume ${params.name} ${params.location.join(' ')} ${params.size.join(' ')}`);
695
- if (params.reverbEffect) {
696
- commands.push(`SetReverbEffect ${params.name} ${params.reverbEffect}`);
697
- }
698
- if (params.volume !== undefined) {
699
- commands.push(`SetReverbVolume ${params.name} ${params.volume}`);
252
+ async createReverbZone(_params) {
253
+ if (!this.automationBridge) {
254
+ throw new Error('Automation Bridge not available. Creating reverb zones requires plugin support.');
700
255
  }
701
- if (params.fadeTime !== undefined) {
702
- commands.push(`SetReverbFadeTime ${params.name} ${params.fadeTime}`);
256
+ try {
257
+ const response = await this.automationBridge.sendAutomationRequest('create_reverb_zone', {
258
+ name: _params.name,
259
+ location: _params.location,
260
+ size: _params.size,
261
+ reverbEffect: _params.reverbEffect,
262
+ volume: _params.volume,
263
+ fadeTime: _params.fadeTime
264
+ });
265
+ return response.success
266
+ ? { success: true, message: response.message || 'Reverb zone created', ...(response.result || {}) }
267
+ : { success: false, error: response.error || response.message || 'Failed to create reverb zone' };
703
268
  }
704
- for (const cmd of commands) {
705
- await this.bridge.executeConsoleCommand(cmd);
269
+ catch (error) {
270
+ return { success: false, error: `Failed to create reverb zone: ${error instanceof Error ? error.message : String(error)}` };
706
271
  }
707
- return { success: true, message: `Reverb zone ${params.name} created` };
708
272
  }
709
- // Audio analysis
710
- async enableAudioAnalysis(params) {
711
- const commands = [];
712
- commands.push(`EnableAudioAnalysis ${params.enabled}`);
713
- if (params.enabled && params.fftSize) {
714
- commands.push(`SetFFTSize ${params.fftSize}`);
273
+ async enableAudioAnalysis(_params) {
274
+ if (!this.automationBridge) {
275
+ throw new Error('Automation Bridge not available. Audio analysis controls require plugin support.');
715
276
  }
716
- if (params.enabled && params.outputType) {
717
- commands.push(`SetAudioAnalysisOutput ${params.outputType}`);
277
+ try {
278
+ const response = await this.automationBridge.sendAutomationRequest('enable_audio_analysis', {
279
+ enabled: _params.enabled,
280
+ fftSize: _params.fftSize,
281
+ outputType: _params.outputType
282
+ });
283
+ return response.success
284
+ ? { success: true, message: response.message || `Audio analysis ${_params.enabled ? 'enabled' : 'disabled'}`, ...(response.result || {}) }
285
+ : { success: false, error: response.error || response.message || 'Failed to enable audio analysis' };
718
286
  }
719
- for (const cmd of commands) {
720
- await this.bridge.executeConsoleCommand(cmd);
287
+ catch (error) {
288
+ return { success: false, error: `Failed to enable audio analysis: ${error instanceof Error ? error.message : String(error)}` };
721
289
  }
722
- return { success: true, message: `Audio analysis ${params.enabled ? 'enabled' : 'disabled'}` };
723
290
  }
724
- // Stop all sounds
725
291
  async stopAllSounds() {
726
292
  return this.bridge.executeConsoleCommand('StopAllSounds');
727
293
  }
728
- // Fade sound
729
- async fadeSound(params) {
730
- const type = params.fadeType || 'FadeTo';
731
- const command = `${type}Sound ${params.soundName} ${params.targetVolume} ${params.fadeTime}`;
732
- return this.bridge.executeConsoleCommand(command);
294
+ async fadeSound(_params) {
295
+ if (!this.automationBridge) {
296
+ throw new Error('Automation Bridge not available. Fading sound requires plugin support.');
297
+ }
298
+ try {
299
+ const response = await this.automationBridge.sendAutomationRequest('fade_sound', {
300
+ soundName: _params.soundName,
301
+ targetVolume: _params.targetVolume,
302
+ fadeTime: _params.fadeTime,
303
+ fadeType: _params.fadeType || 'FadeTo'
304
+ });
305
+ return response.success
306
+ ? { success: true, message: response.message || 'Sound faded', ...(response.result || {}) }
307
+ : { success: false, error: response.error || response.message || 'Failed to fade sound' };
308
+ }
309
+ catch (error) {
310
+ return { success: false, error: `Failed to fade sound: ${error instanceof Error ? error.message : String(error)}` };
311
+ }
733
312
  }
734
- // Set doppler effect
735
- async setDopplerEffect(params) {
736
- const commands = [];
737
- commands.push(`EnableDoppler ${params.enabled}`);
738
- if (params.scale !== undefined) {
739
- commands.push(`SetDopplerScale ${params.scale}`);
313
+ async setDopplerEffect(_params) {
314
+ if (!this.automationBridge) {
315
+ throw new Error('Automation Bridge not available. Doppler effect controls require plugin support.');
740
316
  }
741
- for (const cmd of commands) {
742
- await this.bridge.executeConsoleCommand(cmd);
317
+ try {
318
+ const response = await this.automationBridge.sendAutomationRequest('set_doppler_effect', {
319
+ enabled: _params.enabled,
320
+ scale: _params.scale ?? 1.0
321
+ });
322
+ return response.success
323
+ ? { success: true, message: response.message || `Doppler effect ${_params.enabled ? 'enabled' : 'disabled'}`, ...(response.result || {}) }
324
+ : { success: false, error: response.error || response.message || 'Failed to set doppler effect' };
325
+ }
326
+ catch (error) {
327
+ return { success: false, error: `Failed to set doppler effect: ${error instanceof Error ? error.message : String(error)}` };
743
328
  }
744
- return { success: true, message: `Doppler effect ${params.enabled ? 'enabled' : 'disabled'}` };
745
329
  }
746
- // Audio occlusion
747
- async setAudioOcclusion(params) {
748
- const commands = [];
749
- commands.push(`EnableAudioOcclusion ${params.enabled}`);
750
- if (params.lowPassFilterFrequency !== undefined) {
751
- commands.push(`SetOcclusionLowPassFilter ${params.lowPassFilterFrequency}`);
330
+ async setAudioOcclusion(_params) {
331
+ if (!this.automationBridge) {
332
+ throw new Error('Automation Bridge not available. Audio occlusion controls require plugin support.');
752
333
  }
753
- if (params.volumeAttenuation !== undefined) {
754
- commands.push(`SetOcclusionVolumeAttenuation ${params.volumeAttenuation}`);
334
+ try {
335
+ const response = await this.automationBridge.sendAutomationRequest('set_audio_occlusion', {
336
+ enabled: _params.enabled,
337
+ lowPassFilterFrequency: _params.lowPassFilterFrequency,
338
+ volumeAttenuation: _params.volumeAttenuation
339
+ });
340
+ return response.success
341
+ ? { success: true, message: response.message || `Audio occlusion ${_params.enabled ? 'enabled' : 'disabled'}`, ...(response.result || {}) }
342
+ : { success: false, error: response.error || response.message || 'Failed to set audio occlusion' };
755
343
  }
756
- for (const cmd of commands) {
757
- await this.bridge.executeConsoleCommand(cmd);
344
+ catch (error) {
345
+ return { success: false, error: `Failed to set audio occlusion: ${error instanceof Error ? error.message : String(error)}` };
758
346
  }
759
- return { success: true, message: `Audio occlusion ${params.enabled ? 'enabled' : 'disabled'}` };
760
347
  }
761
348
  }
762
349
  //# sourceMappingURL=audio.js.map