unreal-engine-mcp-server 0.4.7 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +26 -0
- package/.env.production +38 -7
- package/.eslintrc.json +0 -54
- package/.eslintrc.override.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- package/.github/copilot-instructions.md +478 -45
- package/.github/dependabot.yml +19 -0
- package/.github/labeler.yml +24 -0
- package/.github/labels.yml +70 -0
- package/.github/pull_request_template.md +42 -0
- package/.github/release-drafter-config.yml +51 -0
- package/.github/workflows/auto-merge.yml +38 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/dependency-review.yml +17 -0
- package/.github/workflows/gemini-issue-triage.yml +172 -0
- package/.github/workflows/greetings.yml +27 -0
- package/.github/workflows/labeler.yml +17 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +13 -7
- package/.github/workflows/release-drafter.yml +23 -0
- package/.github/workflows/release.yml +112 -0
- package/.github/workflows/semantic-pull-request.yml +35 -0
- package/.github/workflows/smoke-test.yml +36 -0
- package/.github/workflows/stale.yml +28 -0
- package/CHANGELOG.md +338 -31
- package/CONTRIBUTING.md +140 -0
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +189 -128
- package/claude_desktop_config_example.json +7 -6
- package/dist/automation/bridge.d.ts +50 -0
- package/dist/automation/bridge.js +452 -0
- package/dist/automation/connection-manager.d.ts +23 -0
- package/dist/automation/connection-manager.js +107 -0
- package/dist/automation/handshake.d.ts +11 -0
- package/dist/automation/handshake.js +89 -0
- package/dist/automation/index.d.ts +3 -0
- package/dist/automation/index.js +3 -0
- package/dist/automation/message-handler.d.ts +12 -0
- package/dist/automation/message-handler.js +149 -0
- package/dist/automation/request-tracker.d.ts +25 -0
- package/dist/automation/request-tracker.js +98 -0
- package/dist/automation/types.d.ts +130 -0
- package/dist/automation/types.js +2 -0
- package/dist/cli.js +32 -5
- package/dist/config.d.ts +26 -0
- package/dist/config.js +59 -0
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +16 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +746 -0
- package/dist/graphql/schema.d.ts +5 -0
- package/dist/graphql/schema.js +437 -0
- package/dist/graphql/server.d.ts +26 -0
- package/dist/graphql/server.js +117 -0
- package/dist/graphql/types.d.ts +9 -0
- package/dist/graphql/types.js +2 -0
- package/dist/handlers/resource-handlers.d.ts +20 -0
- package/dist/handlers/resource-handlers.js +180 -0
- package/dist/index.d.ts +33 -18
- package/dist/index.js +130 -619
- package/dist/resources/actors.d.ts +17 -12
- package/dist/resources/actors.js +56 -76
- package/dist/resources/assets.d.ts +6 -14
- package/dist/resources/assets.js +115 -147
- package/dist/resources/levels.d.ts +13 -13
- package/dist/resources/levels.js +25 -34
- package/dist/server/resource-registry.d.ts +20 -0
- package/dist/server/resource-registry.js +37 -0
- package/dist/server/tool-registry.d.ts +23 -0
- package/dist/server/tool-registry.js +322 -0
- package/dist/server-setup.d.ts +20 -0
- package/dist/server-setup.js +71 -0
- package/dist/services/health-monitor.d.ts +34 -0
- package/dist/services/health-monitor.js +105 -0
- package/dist/services/metrics-server.d.ts +11 -0
- package/dist/services/metrics-server.js +105 -0
- package/dist/tools/actors.d.ts +163 -9
- package/dist/tools/actors.js +356 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +75 -29
- package/dist/tools/assets.js +265 -284
- package/dist/tools/audio.d.ts +102 -42
- package/dist/tools/audio.js +272 -685
- package/dist/tools/base-tool.d.ts +17 -0
- package/dist/tools/base-tool.js +46 -0
- package/dist/tools/behavior-tree.d.ts +94 -0
- package/dist/tools/behavior-tree.js +39 -0
- package/dist/tools/blueprint.d.ts +208 -126
- package/dist/tools/blueprint.js +685 -832
- package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
- package/dist/tools/consolidated-tool-definitions.js +829 -496
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +198 -1027
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +13 -0
- package/dist/tools/dynamic-handler-registry.js +23 -0
- package/dist/tools/editor.d.ts +30 -83
- package/dist/tools/editor.js +247 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +30 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +65 -99
- package/dist/tools/foliage.js +221 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +227 -0
- package/dist/tools/handlers/animation-handlers.d.ts +3 -0
- package/dist/tools/handlers/animation-handlers.js +185 -0
- package/dist/tools/handlers/argument-helper.d.ts +16 -0
- package/dist/tools/handlers/argument-helper.js +80 -0
- package/dist/tools/handlers/asset-handlers.d.ts +3 -0
- package/dist/tools/handlers/asset-handlers.js +496 -0
- package/dist/tools/handlers/audio-handlers.d.ts +3 -0
- package/dist/tools/handlers/audio-handlers.js +166 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
- package/dist/tools/handlers/blueprint-handlers.js +358 -0
- package/dist/tools/handlers/common-handlers.d.ts +14 -0
- package/dist/tools/handlers/common-handlers.js +56 -0
- package/dist/tools/handlers/editor-handlers.d.ts +3 -0
- package/dist/tools/handlers/editor-handlers.js +119 -0
- package/dist/tools/handlers/effect-handlers.d.ts +3 -0
- package/dist/tools/handlers/effect-handlers.js +171 -0
- package/dist/tools/handlers/environment-handlers.d.ts +3 -0
- package/dist/tools/handlers/environment-handlers.js +170 -0
- package/dist/tools/handlers/graph-handlers.d.ts +3 -0
- package/dist/tools/handlers/graph-handlers.js +90 -0
- package/dist/tools/handlers/input-handlers.d.ts +3 -0
- package/dist/tools/handlers/input-handlers.js +21 -0
- package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
- package/dist/tools/handlers/inspect-handlers.js +383 -0
- package/dist/tools/handlers/level-handlers.d.ts +3 -0
- package/dist/tools/handlers/level-handlers.js +237 -0
- package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
- package/dist/tools/handlers/lighting-handlers.js +144 -0
- package/dist/tools/handlers/performance-handlers.d.ts +3 -0
- package/dist/tools/handlers/performance-handlers.js +130 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
- package/dist/tools/handlers/pipeline-handlers.js +110 -0
- package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
- package/dist/tools/handlers/sequence-handlers.js +376 -0
- package/dist/tools/handlers/system-handlers.d.ts +4 -0
- package/dist/tools/handlers/system-handlers.js +506 -0
- package/dist/tools/input.d.ts +19 -0
- package/dist/tools/input.js +89 -0
- package/dist/tools/introspection.d.ts +103 -40
- package/dist/tools/introspection.js +425 -568
- package/dist/tools/landscape.d.ts +54 -93
- package/dist/tools/landscape.js +284 -409
- package/dist/tools/level.d.ts +66 -27
- package/dist/tools/level.js +647 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +445 -943
- package/dist/tools/logs.d.ts +3 -3
- package/dist/tools/logs.js +5 -57
- package/dist/tools/materials.d.ts +91 -24
- package/dist/tools/materials.js +194 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +267 -182
- package/dist/tools/performance.d.ts +27 -13
- package/dist/tools/performance.js +203 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +175 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +85 -60
- package/dist/tools/sequence.js +208 -747
- package/dist/tools/tool-definition-utils.d.ts +59 -0
- package/dist/tools/tool-definition-utils.js +35 -0
- package/dist/tools/ui.d.ts +64 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- package/dist/types/tool-interfaces.d.ts +898 -0
- package/dist/types/tool-interfaces.js +2 -0
- package/dist/types/tool-types.d.ts +183 -19
- package/dist/types/tool-types.js +0 -4
- package/dist/unreal-bridge.d.ts +24 -131
- package/dist/unreal-bridge.js +364 -1506
- package/dist/utils/command-validator.d.ts +9 -0
- package/dist/utils/command-validator.js +68 -0
- package/dist/utils/elicitation.d.ts +1 -1
- package/dist/utils/elicitation.js +12 -15
- package/dist/utils/error-handler.d.ts +2 -51
- package/dist/utils/error-handler.js +11 -87
- package/dist/utils/ini-reader.d.ts +3 -0
- package/dist/utils/ini-reader.js +69 -0
- package/dist/utils/logger.js +9 -6
- package/dist/utils/normalize.d.ts +3 -0
- package/dist/utils/normalize.js +56 -0
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +27 -0
- package/dist/utils/response-validator.d.ts +3 -24
- package/dist/utils/response-validator.js +130 -81
- package/dist/utils/result-helpers.d.ts +4 -5
- package/dist/utils/result-helpers.js +15 -16
- package/dist/utils/safe-json.js +5 -11
- package/dist/utils/unreal-command-queue.d.ts +24 -0
- package/dist/utils/unreal-command-queue.js +120 -0
- package/dist/utils/validation.d.ts +0 -40
- package/dist/utils/validation.js +1 -78
- package/dist/wasm/index.d.ts +70 -0
- package/dist/wasm/index.js +535 -0
- package/docs/GraphQL-API.md +888 -0
- package/docs/Migration-Guide-v0.5.0.md +684 -0
- package/docs/Roadmap.md +53 -0
- package/docs/WebAssembly-Integration.md +628 -0
- package/docs/editor-plugin-extension.md +370 -0
- package/docs/handler-mapping.md +242 -0
- package/docs/native-automation-progress.md +128 -0
- package/docs/testing-guide.md +423 -0
- package/mcp-config-example.json +6 -6
- package/package.json +67 -28
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
- package/scripts/check-unreal-connection.mjs +19 -0
- package/scripts/clean-tmp.js +23 -0
- package/scripts/patch-wasm.js +26 -0
- package/scripts/run-all-tests.mjs +136 -0
- package/scripts/smoke-test.ts +94 -0
- package/scripts/sync-mcp-plugin.js +143 -0
- package/scripts/test-no-plugin-alternates.mjs +113 -0
- package/scripts/validate-server.js +46 -0
- package/scripts/verify-automation-bridge.js +200 -0
- package/server.json +58 -21
- package/src/automation/bridge.ts +558 -0
- package/src/automation/connection-manager.ts +130 -0
- package/src/automation/handshake.ts +99 -0
- package/src/automation/index.ts +2 -0
- package/src/automation/message-handler.ts +167 -0
- package/src/automation/request-tracker.ts +123 -0
- package/src/automation/types.ts +107 -0
- package/src/cli.ts +33 -6
- package/src/config.ts +73 -0
- package/src/constants.ts +19 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +1008 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +156 -0
- package/src/graphql/types.ts +10 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +166 -664
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +148 -134
- package/src/resources/levels.ts +28 -33
- package/src/server/resource-registry.ts +47 -0
- package/src/server/tool-registry.ts +354 -0
- package/src/server-setup.ts +114 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +426 -323
- package/src/tools/animation.ts +672 -461
- package/src/tools/assets.ts +364 -289
- package/src/tools/audio.ts +323 -766
- package/src/tools/base-tool.ts +52 -0
- package/src/tools/behavior-tree.ts +45 -0
- package/src/tools/blueprint.ts +792 -970
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +258 -1146
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +33 -0
- package/src/tools/editor.ts +329 -253
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +281 -0
- package/src/tools/foliage.ts +330 -392
- package/src/tools/handlers/actor-handlers.ts +265 -0
- package/src/tools/handlers/animation-handlers.ts +237 -0
- package/src/tools/handlers/argument-helper.ts +142 -0
- package/src/tools/handlers/asset-handlers.ts +532 -0
- package/src/tools/handlers/audio-handlers.ts +194 -0
- package/src/tools/handlers/blueprint-handlers.ts +380 -0
- package/src/tools/handlers/common-handlers.ts +87 -0
- package/src/tools/handlers/editor-handlers.ts +123 -0
- package/src/tools/handlers/effect-handlers.ts +220 -0
- package/src/tools/handlers/environment-handlers.ts +183 -0
- package/src/tools/handlers/graph-handlers.ts +116 -0
- package/src/tools/handlers/input-handlers.ts +28 -0
- package/src/tools/handlers/inspect-handlers.ts +450 -0
- package/src/tools/handlers/level-handlers.ts +252 -0
- package/src/tools/handlers/lighting-handlers.ts +147 -0
- package/src/tools/handlers/performance-handlers.ts +132 -0
- package/src/tools/handlers/pipeline-handlers.ts +127 -0
- package/src/tools/handlers/sequence-handlers.ts +415 -0
- package/src/tools/handlers/system-handlers.ts +564 -0
- package/src/tools/input.ts +101 -0
- package/src/tools/introspection.ts +493 -584
- package/src/tools/landscape.ts +418 -507
- package/src/tools/level.ts +786 -708
- package/src/tools/lighting.ts +588 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +237 -121
- package/src/tools/niagara.ts +335 -168
- package/src/tools/performance.ts +320 -169
- package/src/tools/physics.ts +274 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +276 -820
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +205 -283
- package/src/types/automation-responses.ts +119 -0
- package/src/types/env.ts +0 -10
- package/src/types/responses.ts +355 -0
- package/src/types/tool-interfaces.ts +250 -0
- package/src/types/tool-types.ts +243 -21
- package/src/unreal-bridge.ts +460 -1550
- package/src/utils/command-validator.ts +76 -0
- package/src/utils/elicitation.ts +10 -7
- package/src/utils/error-handler.ts +14 -90
- package/src/utils/ini-reader.ts +86 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/normalize.test.ts +162 -0
- package/src/utils/normalize.ts +60 -0
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +44 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.test.ts +184 -0
- package/src/utils/validation.ts +4 -1
- package/src/wasm/index.ts +838 -0
- package/test-server.mjs +100 -0
- package/tests/run-unreal-tool-tests.mjs +242 -14
- package/tests/test-animation.mjs +369 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +417 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +444 -0
- package/tests/test-blueprint-graph.mjs +410 -0
- package/tests/test-blueprint.mjs +577 -0
- package/tests/test-client-mode.mjs +86 -0
- package/tests/test-console-command.mjs +56 -0
- package/tests/test-control-actor.mjs +425 -0
- package/tests/test-control-editor.mjs +112 -0
- package/tests/test-graphql.mjs +372 -0
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +302 -0
- package/tests/test-landscape.mjs +316 -0
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +89 -0
- package/tests/test-materials.mjs +356 -0
- package/tests/test-niagara.mjs +185 -0
- package/tests/test-no-inline-python.mjs +122 -0
- package/tests/test-performance.mjs +539 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-sequence.mjs +104 -0
- package/tests/test-system.mjs +96 -0
- package/tests/test-wasm.mjs +283 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/vitest.config.ts +35 -0
- package/wasm/Cargo.lock +363 -0
- package/wasm/Cargo.toml +42 -0
- package/wasm/LICENSE +21 -0
- package/wasm/README.md +253 -0
- package/wasm/src/dependency_resolver.rs +377 -0
- package/wasm/src/lib.rs +153 -0
- package/wasm/src/property_parser.rs +271 -0
- package/wasm/src/transform_math.rs +396 -0
- package/wasm/tests/integration.rs +109 -0
- package/.github/workflows/smithery-build.yml +0 -29
- package/dist/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- package/dist/tools/build_environment_advanced.d.ts +0 -65
- package/dist/tools/build_environment_advanced.js +0 -633
- package/dist/tools/rc.d.ts +0 -110
- package/dist/tools/rc.js +0 -437
- package/dist/tools/visual.d.ts +0 -40
- package/dist/tools/visual.js +0 -282
- package/dist/utils/http.d.ts +0 -6
- package/dist/utils/http.js +0 -151
- package/dist/utils/python-output.d.ts +0 -18
- package/dist/utils/python-output.js +0 -290
- package/dist/utils/python.d.ts +0 -2
- package/dist/utils/python.js +0 -4
- package/dist/utils/stdio-redirect.d.ts +0 -2
- package/dist/utils/stdio-redirect.js +0 -20
- package/docs/unreal-tool-test-cases.md +0 -574
- package/smithery.yaml +0 -29
- package/src/prompts/index.ts +0 -249
- package/src/tools/build_environment_advanced.ts +0 -732
- package/src/tools/rc.ts +0 -515
- package/src/tools/visual.ts +0 -281
- package/src/utils/http.ts +0 -187
- package/src/utils/python-output.ts +0 -351
- package/src/utils/python.ts +0 -3
- package/src/utils/stdio-redirect.ts +0 -18
package/dist/tools/audio.js
CHANGED
|
@@ -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
|
-
|
|
3
|
+
automationBridge;
|
|
4
|
+
constructor(bridge, automationBridge) {
|
|
7
5
|
this.bridge = bridge;
|
|
6
|
+
this.automationBridge = automationBridge;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
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
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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 (
|
|
304
|
-
return { success: false, error: `Failed to create sound cue: ${
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
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
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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 (
|
|
390
|
-
return { success: false, error: `Failed to play sound: ${
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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 (
|
|
513
|
-
return { success: false, error: `Failed to play
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
|
|
524
|
-
|
|
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
|
-
|
|
527
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
539
|
-
|
|
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
|
-
|
|
542
|
-
|
|
148
|
+
catch (error) {
|
|
149
|
+
return { success: false, error: `Failed to set sound attenuation: ${error instanceof Error ? error.message : String(error)}` };
|
|
543
150
|
}
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
548
|
-
await this.
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
-
|
|
572
|
-
await this.
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
598
|
-
await this.
|
|
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(
|
|
608
|
-
|
|
609
|
-
|
|
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
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
684
|
-
|
|
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
|
-
|
|
687
|
-
|
|
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
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
-
|
|
702
|
-
|
|
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
|
-
|
|
705
|
-
|
|
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
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
-
|
|
717
|
-
|
|
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
|
-
|
|
720
|
-
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
|
|
742
|
-
await this.
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
754
|
-
|
|
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
|
-
|
|
757
|
-
|
|
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
|