unreal-engine-mcp-server 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.yml +148 -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 +23 -0
- package/.github/workflows/labeler.yml +16 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +12 -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 +267 -31
- package/CONTRIBUTING.md +140 -0
- package/README.md +166 -71
- 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 +27 -0
- package/dist/config.js +60 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +12 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +743 -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 +115 -0
- package/dist/graphql/types.d.ts +7 -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 +31 -18
- package/dist/index.js +119 -619
- package/dist/prompts/index.js +4 -4
- 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 +21 -0
- package/dist/server-setup.js +111 -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 +147 -9
- package/dist/tools/actors.js +350 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +117 -19
- package/dist/tools/assets.js +259 -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/helpers.d.ts +29 -0
- package/dist/tools/blueprint/helpers.js +182 -0
- package/dist/tools/blueprint.d.ts +228 -118
- 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 +211 -1026
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +11 -0
- package/dist/tools/dynamic-handler-registry.js +101 -0
- package/dist/tools/editor.d.ts +139 -18
- package/dist/tools/editor.js +239 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +36 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +105 -14
- package/dist/tools/foliage.js +219 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +232 -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 +97 -36
- package/dist/tools/landscape.js +280 -409
- package/dist/tools/level.d.ts +130 -10
- package/dist/tools/level.js +639 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +441 -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 +190 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +232 -182
- package/dist/tools/performance.d.ts +27 -12
- package/dist/tools/performance.js +204 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +171 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +73 -48
- package/dist/tools/sequence.js +196 -748
- 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 +66 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- 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 +67 -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/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +33 -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 +692 -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 +60 -27
- 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 +131 -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 +57 -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 +12 -0
- package/src/graphql/resolvers.ts +1010 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +154 -0
- package/src/graphql/types.ts +7 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +152 -663
- package/src/prompts/index.ts +4 -4
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +147 -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 +148 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +417 -322
- package/src/tools/animation.ts +671 -461
- package/src/tools/assets.ts +353 -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/helpers.ts +189 -0
- package/src/tools/blueprint.ts +787 -965
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +272 -1139
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +151 -0
- package/src/tools/editor.ts +309 -246
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +287 -0
- package/src/tools/foliage.ts +314 -379
- package/src/tools/handlers/actor-handlers.ts +271 -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 +394 -489
- package/src/tools/level.ts +752 -694
- package/src/tools/lighting.ts +583 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +231 -121
- package/src/tools/niagara.ts +293 -168
- package/src/tools/performance.ts +320 -168
- package/src/tools/physics.ts +268 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +255 -815
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +207 -283
- package/src/types/env.ts +0 -10
- 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 +75 -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.ts +60 -0
- package/src/utils/response-factory.ts +39 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -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 +44 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-audio.mjs +219 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +261 -0
- package/tests/test-blueprint-events.mjs +35 -0
- package/tests/test-blueprint-graph.mjs +79 -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 +80 -0
- package/tests/test-extra-tools.mjs +38 -0
- package/tests/test-graphql.mjs +322 -0
- package/tests/test-inspect.mjs +72 -0
- package/tests/test-landscape.mjs +60 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +70 -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-plugin-handshake.mjs +82 -0
- package/tests/test-render.mjs +33 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-search-assets.mjs +66 -0
- package/tests/test-sequence.mjs +68 -0
- package/tests/test-system.mjs +57 -0
- package/tests/test-wasm.mjs +193 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- 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/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/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/sequence.js
CHANGED
|
@@ -1,797 +1,245 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export class SequenceTools {
|
|
4
|
-
bridge;
|
|
5
|
-
log = new Logger('SequenceTools');
|
|
1
|
+
import { BaseTool } from './base-tool.js';
|
|
2
|
+
export class SequenceTools extends BaseTool {
|
|
6
3
|
sequenceCache = new Map();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
async ensureSequencerPrerequisites(operation) {
|
|
13
|
-
const missing = await this.bridge.ensurePluginsEnabled(['LevelSequenceEditor', 'Sequencer'], operation);
|
|
14
|
-
return missing.length ? missing : null;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Execute with retry logic for transient failures
|
|
18
|
-
*/
|
|
19
|
-
async executeWithRetry(operation, operationName) {
|
|
20
|
-
let lastError;
|
|
21
|
-
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
|
|
22
|
-
try {
|
|
23
|
-
return await operation();
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
lastError = error;
|
|
27
|
-
this.log.warn(`${operationName} attempt ${attempt} failed: ${error.message || error}`);
|
|
28
|
-
if (attempt < this.retryAttempts) {
|
|
29
|
-
await new Promise(resolve => setTimeout(resolve, this.retryDelay * attempt));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
throw lastError;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Parse Python execution result with better error handling
|
|
37
|
-
*/
|
|
38
|
-
parsePythonResult(resp, operationName) {
|
|
39
|
-
const interpreted = interpretStandardResult(resp, {
|
|
40
|
-
successMessage: `${operationName} succeeded`,
|
|
41
|
-
failureMessage: `${operationName} failed`
|
|
42
|
-
});
|
|
43
|
-
if (interpreted.success) {
|
|
44
|
-
return {
|
|
45
|
-
...interpreted.payload,
|
|
46
|
-
success: true
|
|
47
|
-
};
|
|
4
|
+
activeSequencePath;
|
|
5
|
+
resolveSequencePath(explicitPath) {
|
|
6
|
+
if (typeof explicitPath === 'string' && explicitPath.trim().length > 0) {
|
|
7
|
+
return explicitPath.trim();
|
|
48
8
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
9
|
+
return this.activeSequencePath;
|
|
10
|
+
}
|
|
11
|
+
async sendAction(action, payload = {}, timeoutMs) {
|
|
12
|
+
const envDefault = Number(process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '120000');
|
|
13
|
+
const defaultTimeout = Number.isFinite(envDefault) && envDefault > 0 ? envDefault : 120000;
|
|
14
|
+
const finalTimeout = typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : defaultTimeout;
|
|
15
|
+
try {
|
|
16
|
+
const response = await this.sendAutomationRequest(action, payload, { timeoutMs: finalTimeout, waitForEvent: false });
|
|
17
|
+
const success = response && response.success !== false;
|
|
18
|
+
const result = response.result ?? response;
|
|
19
|
+
return { success, message: response.message ?? undefined, error: response.success === false ? (response.error ?? response.message) : undefined, result, requestId: response.requestId };
|
|
56
20
|
}
|
|
57
|
-
|
|
58
|
-
return { success: false, error:
|
|
21
|
+
catch (err) {
|
|
22
|
+
return { success: false, error: String(err), message: String(err) };
|
|
59
23
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
: (cleanedOutput ?? '').substring(0, 200).trim();
|
|
67
|
-
return detail ? `${baseError}: ${detail}` : baseError;
|
|
68
|
-
})()
|
|
69
|
-
};
|
|
24
|
+
}
|
|
25
|
+
isUnknownActionResponse(res) {
|
|
26
|
+
if (!res)
|
|
27
|
+
return false;
|
|
28
|
+
const txt = String((res.error ?? res.message ?? '')).toLowerCase();
|
|
29
|
+
return txt.includes('unknown_action') || txt.includes('unknown automation action') || txt.includes('not_implemented') || txt === 'unknown_plugin_action';
|
|
70
30
|
}
|
|
71
31
|
async create(params) {
|
|
72
32
|
const name = params.name?.trim();
|
|
73
33
|
const base = (params.path || '/Game/Sequences').replace(/\/$/, '');
|
|
74
34
|
if (!name)
|
|
75
35
|
return { success: false, error: 'name is required' };
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
simulated: true,
|
|
83
|
-
sequencePath,
|
|
84
|
-
message: 'Sequencer plugins disabled; reported simulated sequence creation.',
|
|
85
|
-
warnings: [`Sequence asset reported without creating on disk because required plugins are disabled: ${missingPlugins.join(', ')}`]
|
|
86
|
-
};
|
|
36
|
+
const payload = { name, path: base };
|
|
37
|
+
const resp = await this.sendAction('sequence_create', payload, params.timeoutMs);
|
|
38
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
39
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_create' };
|
|
87
40
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
name = r"${name}"
|
|
91
|
-
base = r"${base}"
|
|
92
|
-
full = f"{base}/{name}"
|
|
93
|
-
try:
|
|
94
|
-
# Ensure directory exists
|
|
95
|
-
try:
|
|
96
|
-
if not unreal.EditorAssetLibrary.does_directory_exist(base):
|
|
97
|
-
unreal.EditorAssetLibrary.make_directory(base)
|
|
98
|
-
except Exception:
|
|
99
|
-
pass
|
|
100
|
-
|
|
101
|
-
if unreal.EditorAssetLibrary.does_asset_exist(full):
|
|
102
|
-
print('RESULT:' + json.dumps({'success': True, 'sequencePath': full, 'existing': True}))
|
|
103
|
-
else:
|
|
104
|
-
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
|
|
105
|
-
factory = unreal.LevelSequenceFactoryNew()
|
|
106
|
-
seq = asset_tools.create_asset(asset_name=name, package_path=base, asset_class=unreal.LevelSequence, factory=factory)
|
|
107
|
-
if seq:
|
|
108
|
-
unreal.EditorAssetLibrary.save_asset(full)
|
|
109
|
-
print('RESULT:' + json.dumps({'success': True, 'sequencePath': full}))
|
|
110
|
-
else:
|
|
111
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Create returned None'}))
|
|
112
|
-
except Exception as e:
|
|
113
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
114
|
-
`.trim();
|
|
115
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'createSequence');
|
|
116
|
-
const result = this.parsePythonResult(resp, 'createSequence');
|
|
117
|
-
// Cache the sequence if successful
|
|
118
|
-
if (result.success && result.sequencePath) {
|
|
119
|
-
const sequence = {
|
|
120
|
-
path: result.sequencePath,
|
|
121
|
-
name: name
|
|
122
|
-
};
|
|
41
|
+
if (resp.success && resp.result && resp.result.sequencePath) {
|
|
42
|
+
const sequence = { path: resp.result.sequencePath, name };
|
|
123
43
|
this.sequenceCache.set(sequence.path, sequence);
|
|
44
|
+
return { ...resp, sequence: resp.result.sequencePath };
|
|
124
45
|
}
|
|
125
|
-
return
|
|
46
|
+
return resp;
|
|
126
47
|
}
|
|
127
48
|
async open(params) {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
133
|
-
};
|
|
49
|
+
const path = params.path?.trim();
|
|
50
|
+
const resp = await this.sendAction('sequence_open', { path });
|
|
51
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
52
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_open' };
|
|
134
53
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
seq = unreal.load_asset(path)
|
|
140
|
-
if not seq:
|
|
141
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Sequence not found'}))
|
|
142
|
-
else:
|
|
143
|
-
unreal.LevelSequenceEditorBlueprintLibrary.open_level_sequence(seq)
|
|
144
|
-
print('RESULT:' + json.dumps({'success': True, 'sequencePath': path}))
|
|
145
|
-
except Exception as e:
|
|
146
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
147
|
-
`.trim();
|
|
148
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'openSequence');
|
|
149
|
-
return this.parsePythonResult(resp, 'openSequence');
|
|
54
|
+
if (resp && resp.success !== false && path) {
|
|
55
|
+
this.activeSequencePath = path;
|
|
56
|
+
}
|
|
57
|
+
return resp;
|
|
150
58
|
}
|
|
151
59
|
async addCamera(params) {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
success: true,
|
|
157
|
-
simulated: true,
|
|
158
|
-
cameraBindingId: 'simulated_camera',
|
|
159
|
-
cameraName: 'SimulatedCamera',
|
|
160
|
-
warnings: [`Camera binding simulated because required plugins are disabled: ${missingPlugins.join(', ')}`]
|
|
161
|
-
};
|
|
60
|
+
const path = this.resolveSequencePath(params.path);
|
|
61
|
+
const resp = await this.sendAction('sequence_add_camera', { path, spawnable: params.spawnable !== false });
|
|
62
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
63
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_camera' };
|
|
162
64
|
}
|
|
163
|
-
|
|
164
|
-
import unreal, json
|
|
165
|
-
try:
|
|
166
|
-
ls = unreal.get_editor_subsystem(unreal.LevelSequenceEditorSubsystem)
|
|
167
|
-
if not ls:
|
|
168
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'LevelSequenceEditorSubsystem unavailable'}))
|
|
169
|
-
else:
|
|
170
|
-
# create_camera returns tuple: (binding_proxy, camera_actor)
|
|
171
|
-
result = ls.create_camera(spawnable=${params.spawnable !== false ? 'True' : 'False'})
|
|
172
|
-
binding_id = ''
|
|
173
|
-
camera_name = ''
|
|
174
|
-
|
|
175
|
-
if result and len(result) >= 2:
|
|
176
|
-
binding_proxy = result[0]
|
|
177
|
-
camera_actor = result[1]
|
|
178
|
-
|
|
179
|
-
# Get the current sequence
|
|
180
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
181
|
-
|
|
182
|
-
if seq and binding_proxy:
|
|
183
|
-
try:
|
|
184
|
-
# Get GUID directly from binding proxy - this is more reliable
|
|
185
|
-
binding_guid = unreal.MovieSceneBindingExtensions.get_id(binding_proxy)
|
|
186
|
-
# The GUID itself is what we need
|
|
187
|
-
binding_id = str(binding_guid).replace('<Guid ', '').replace('>', '').split(' ')[0] if str(binding_guid).startswith('<') else str(binding_guid)
|
|
188
|
-
|
|
189
|
-
# If that didn't work, try the binding object
|
|
190
|
-
if binding_id.startswith('<') or not binding_id:
|
|
191
|
-
binding_obj = unreal.MovieSceneSequenceExtensions.get_binding_id(seq, binding_proxy)
|
|
192
|
-
# Try to extract GUID from the object representation
|
|
193
|
-
obj_str = str(binding_obj)
|
|
194
|
-
if 'guid=' in obj_str:
|
|
195
|
-
binding_id = obj_str.split('guid=')[1].split(',')[0].split('}')[0].strip()
|
|
196
|
-
elif hasattr(binding_obj, 'guid'):
|
|
197
|
-
binding_id = str(binding_obj.guid)
|
|
198
|
-
else:
|
|
199
|
-
# Use a hash of the binding for a consistent ID
|
|
200
|
-
import hashlib
|
|
201
|
-
binding_id = hashlib.md5(str(binding_proxy).encode()).hexdigest()[:8]
|
|
202
|
-
except Exception as e:
|
|
203
|
-
# Generate a unique ID based on camera
|
|
204
|
-
import hashlib
|
|
205
|
-
camera_str = camera_actor.get_name() if camera_actor else 'spawned'
|
|
206
|
-
binding_id = f'cam_{hashlib.md5(camera_str.encode()).hexdigest()[:8]}'
|
|
207
|
-
|
|
208
|
-
if camera_actor:
|
|
209
|
-
try:
|
|
210
|
-
camera_name = camera_actor.get_actor_label()
|
|
211
|
-
except:
|
|
212
|
-
camera_name = 'CineCamera'
|
|
213
|
-
|
|
214
|
-
print('RESULT:' + json.dumps({
|
|
215
|
-
'success': True,
|
|
216
|
-
'cameraBindingId': binding_id,
|
|
217
|
-
'cameraName': camera_name
|
|
218
|
-
}))
|
|
219
|
-
else:
|
|
220
|
-
# Even if result format is different, camera might still be created
|
|
221
|
-
print('RESULT:' + json.dumps({
|
|
222
|
-
'success': True,
|
|
223
|
-
'cameraBindingId': 'camera_created',
|
|
224
|
-
'warning': 'Camera created but binding format unexpected'
|
|
225
|
-
}))
|
|
226
|
-
except Exception as e:
|
|
227
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
228
|
-
`.trim();
|
|
229
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'addCamera');
|
|
230
|
-
return this.parsePythonResult(resp, 'addCamera');
|
|
65
|
+
return resp;
|
|
231
66
|
}
|
|
232
67
|
async addActor(params) {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
238
|
-
};
|
|
68
|
+
const path = this.resolveSequencePath(params.path);
|
|
69
|
+
const resp = await this.sendAction('sequence_add_actor', { path, actorName: params.actorName, createBinding: params.createBinding });
|
|
70
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
71
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_actor' };
|
|
239
72
|
}
|
|
240
|
-
|
|
241
|
-
import unreal, json
|
|
242
|
-
try:
|
|
243
|
-
actor_sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
244
|
-
ls = unreal.get_editor_subsystem(unreal.LevelSequenceEditorSubsystem)
|
|
245
|
-
if not ls or not actor_sub:
|
|
246
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Subsystem unavailable'}))
|
|
247
|
-
else:
|
|
248
|
-
target = None
|
|
249
|
-
actors = actor_sub.get_all_level_actors()
|
|
250
|
-
for a in actors:
|
|
251
|
-
if not a: continue
|
|
252
|
-
label = a.get_actor_label()
|
|
253
|
-
name = a.get_name()
|
|
254
|
-
# Check label, name, and partial matches
|
|
255
|
-
if label == r"${params.actorName}" or name == r"${params.actorName}" or label.startswith(r"${params.actorName}"):
|
|
256
|
-
target = a
|
|
257
|
-
break
|
|
258
|
-
|
|
259
|
-
if not target:
|
|
260
|
-
# Try to find any actors to debug
|
|
261
|
-
actor_info = []
|
|
262
|
-
for a in actors[:5]:
|
|
263
|
-
if a:
|
|
264
|
-
actor_info.append({'label': a.get_actor_label(), 'name': a.get_name()})
|
|
265
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Actor "${params.actorName}" not found. Sample actors: {actor_info}'}))
|
|
266
|
-
else:
|
|
267
|
-
# Make sure we have a focused sequence
|
|
268
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
269
|
-
if seq:
|
|
270
|
-
# Use add_actors method which returns binding proxies
|
|
271
|
-
bindings = ls.add_actors([target])
|
|
272
|
-
binding_info = []
|
|
273
|
-
|
|
274
|
-
# bindings might be a list or might be empty if actor already exists
|
|
275
|
-
if bindings and len(bindings) > 0:
|
|
276
|
-
for binding in bindings:
|
|
277
|
-
try:
|
|
278
|
-
# Get binding name and GUID
|
|
279
|
-
binding_name = unreal.MovieSceneBindingExtensions.get_name(binding)
|
|
280
|
-
binding_guid = unreal.MovieSceneBindingExtensions.get_id(binding)
|
|
281
|
-
|
|
282
|
-
# Extract clean GUID string
|
|
283
|
-
guid_str = str(binding_guid)
|
|
284
|
-
if guid_str.startswith('<Guid '):
|
|
285
|
-
# Extract the actual GUID value from <Guid 'XXXX-XXXX-XXXX-XXXX'>
|
|
286
|
-
guid_clean = guid_str.replace('<Guid ', '').replace('>', '').replace("'", '').split(' ')[0]
|
|
287
|
-
else:
|
|
288
|
-
guid_clean = guid_str
|
|
289
|
-
|
|
290
|
-
binding_info.append({
|
|
291
|
-
'id': guid_clean,
|
|
292
|
-
'guid': guid_clean,
|
|
293
|
-
'name': binding_name if binding_name else target.get_actor_label()
|
|
294
|
-
})
|
|
295
|
-
except Exception as e:
|
|
296
|
-
# If binding methods fail, still count it
|
|
297
|
-
binding_info.append({
|
|
298
|
-
'id': 'binding_' + str(len(binding_info)),
|
|
299
|
-
'name': target.get_actor_label(),
|
|
300
|
-
'error': str(e)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
print('RESULT:' + json.dumps({
|
|
304
|
-
'success': True,
|
|
305
|
-
'count': len(bindings),
|
|
306
|
-
'actorAdded': target.get_actor_label(),
|
|
307
|
-
'bindings': binding_info
|
|
308
|
-
}))
|
|
309
|
-
else:
|
|
310
|
-
# Actor was likely added but no new binding returned (might already exist)
|
|
311
|
-
# Still report success since the actor is in the sequence
|
|
312
|
-
print('RESULT:' + json.dumps({
|
|
313
|
-
'success': True,
|
|
314
|
-
'count': 1,
|
|
315
|
-
'actorAdded': target.get_actor_label(),
|
|
316
|
-
'bindings': [{'name': target.get_actor_label(), 'note': 'Actor added to sequence'}],
|
|
317
|
-
'info': 'Actor processed successfully'
|
|
318
|
-
}))
|
|
319
|
-
else:
|
|
320
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence is currently focused'}))
|
|
321
|
-
except Exception as e:
|
|
322
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
323
|
-
`.trim();
|
|
324
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'addActor');
|
|
325
|
-
return this.parsePythonResult(resp, 'addActor');
|
|
73
|
+
return resp;
|
|
326
74
|
}
|
|
327
|
-
/**
|
|
328
|
-
* Play the current level sequence
|
|
329
|
-
*/
|
|
330
75
|
async play(params) {
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
success: true,
|
|
337
|
-
simulated: true,
|
|
338
|
-
playing: true,
|
|
339
|
-
loopMode: loop || 'default',
|
|
340
|
-
warnings: [`Playback simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
|
|
341
|
-
message: 'Sequencer plugins disabled; playback simulated.'
|
|
342
|
-
};
|
|
76
|
+
const path = this.resolveSequencePath(params?.path);
|
|
77
|
+
const resp = await this.sendAction('sequence_play', { path, startTime: params?.startTime, loopMode: params?.loopMode });
|
|
78
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
79
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_play' };
|
|
343
80
|
}
|
|
344
|
-
|
|
345
|
-
import unreal, json
|
|
346
|
-
|
|
347
|
-
# Helper to resolve SequencerLoopMode from a friendly string
|
|
348
|
-
def _resolve_loop_mode(mode_str):
|
|
349
|
-
try:
|
|
350
|
-
m = str(mode_str).lower()
|
|
351
|
-
slm = unreal.SequencerLoopMode
|
|
352
|
-
if m in ('once','noloop','no_loop'):
|
|
353
|
-
return getattr(slm, 'SLM_NoLoop', getattr(slm, 'NoLoop'))
|
|
354
|
-
if m in ('loop',):
|
|
355
|
-
return getattr(slm, 'SLM_Loop', getattr(slm, 'Loop'))
|
|
356
|
-
if m in ('pingpong','ping_pong'):
|
|
357
|
-
return getattr(slm, 'SLM_PingPong', getattr(slm, 'PingPong'))
|
|
358
|
-
except Exception:
|
|
359
|
-
pass
|
|
360
|
-
return None
|
|
361
|
-
|
|
362
|
-
try:
|
|
363
|
-
unreal.LevelSequenceEditorBlueprintLibrary.play()
|
|
364
|
-
loop_mode = _resolve_loop_mode('${loop}')
|
|
365
|
-
if loop_mode is not None:
|
|
366
|
-
unreal.LevelSequenceEditorBlueprintLibrary.set_loop_mode(loop_mode)
|
|
367
|
-
print('RESULT:' + json.dumps({'success': True, 'playing': True, 'loopMode': '${loop || 'default'}'}))
|
|
368
|
-
except Exception as e:
|
|
369
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
370
|
-
`.trim();
|
|
371
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'playSequence');
|
|
372
|
-
return this.parsePythonResult(resp, 'playSequence');
|
|
81
|
+
return resp;
|
|
373
82
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (missingPlugins) {
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
383
|
-
};
|
|
83
|
+
async pause(params) {
|
|
84
|
+
const path = this.resolveSequencePath(params?.path);
|
|
85
|
+
const resp = await this.sendAction('sequence_pause', { path });
|
|
86
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
87
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_pause' };
|
|
384
88
|
}
|
|
385
|
-
|
|
386
|
-
import unreal, json
|
|
387
|
-
try:
|
|
388
|
-
unreal.LevelSequenceEditorBlueprintLibrary.pause()
|
|
389
|
-
print('RESULT:' + json.dumps({'success': True, 'paused': True}))
|
|
390
|
-
except Exception as e:
|
|
391
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
392
|
-
`.trim();
|
|
393
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'pauseSequence');
|
|
394
|
-
return this.parsePythonResult(resp, 'pauseSequence');
|
|
89
|
+
return resp;
|
|
395
90
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (missingPlugins) {
|
|
402
|
-
return {
|
|
403
|
-
success: false,
|
|
404
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
405
|
-
};
|
|
91
|
+
async stop(params) {
|
|
92
|
+
const path = this.resolveSequencePath(params?.path);
|
|
93
|
+
const resp = await this.sendAction('sequence_stop', { path });
|
|
94
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
95
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_stop' };
|
|
406
96
|
}
|
|
407
|
-
|
|
408
|
-
import unreal, json
|
|
409
|
-
try:
|
|
410
|
-
unreal.LevelSequenceEditorBlueprintLibrary.close_level_sequence()
|
|
411
|
-
print('RESULT:' + json.dumps({'success': True, 'stopped': True}))
|
|
412
|
-
except Exception as e:
|
|
413
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
414
|
-
`.trim();
|
|
415
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'stopSequence');
|
|
416
|
-
return this.parsePythonResult(resp, 'stopSequence');
|
|
97
|
+
return resp;
|
|
417
98
|
}
|
|
418
|
-
/**
|
|
419
|
-
* Set sequence properties including frame rate and length
|
|
420
|
-
*/
|
|
421
99
|
async setSequenceProperties(params) {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
if (params.playbackStart !== undefined || params.playbackEnd !== undefined) {
|
|
433
|
-
changes.push({
|
|
434
|
-
property: 'playbackRange',
|
|
435
|
-
start: params.playbackStart,
|
|
436
|
-
end: params.playbackEnd
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
return {
|
|
440
|
-
success: true,
|
|
441
|
-
simulated: true,
|
|
442
|
-
message: 'Sequencer plugins disabled; property update simulated.',
|
|
443
|
-
warnings: [`Property update simulated because required plugins are disabled: ${missingPlugins.join(', ')}`],
|
|
444
|
-
changes,
|
|
445
|
-
finalProperties: {
|
|
446
|
-
frameRate: params.frameRate ? { numerator: params.frameRate, denominator: 1 } : undefined,
|
|
447
|
-
playbackStart: params.playbackStart,
|
|
448
|
-
playbackEnd: params.playbackEnd,
|
|
449
|
-
duration: params.lengthInFrames
|
|
450
|
-
}
|
|
451
|
-
};
|
|
100
|
+
const payload = {
|
|
101
|
+
path: params.path,
|
|
102
|
+
frameRate: params.frameRate,
|
|
103
|
+
lengthInFrames: params.lengthInFrames,
|
|
104
|
+
playbackStart: params.playbackStart,
|
|
105
|
+
playbackEnd: params.playbackEnd
|
|
106
|
+
};
|
|
107
|
+
const resp = await this.sendAction('sequence_set_properties', payload);
|
|
108
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
109
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_properties' };
|
|
452
110
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
seq = unreal.load_asset(seq_path)
|
|
460
|
-
else:
|
|
461
|
-
# Try to get the currently open sequence
|
|
462
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
463
|
-
|
|
464
|
-
if not seq:
|
|
465
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence found or loaded'}))
|
|
466
|
-
else:
|
|
467
|
-
result = {'success': True, 'changes': []}
|
|
468
|
-
|
|
469
|
-
# Set frame rate if provided
|
|
470
|
-
${params.frameRate ? `
|
|
471
|
-
frame_rate = unreal.FrameRate(numerator=${params.frameRate}, denominator=1)
|
|
472
|
-
unreal.MovieSceneSequenceExtensions.set_display_rate(seq, frame_rate)
|
|
473
|
-
result['changes'].append({'property': 'frameRate', 'value': ${params.frameRate}})
|
|
474
|
-
` : ''}
|
|
475
|
-
|
|
476
|
-
# Set playback range if provided
|
|
477
|
-
${(params.playbackStart !== undefined || params.playbackEnd !== undefined) ? `
|
|
478
|
-
current_range = unreal.MovieSceneSequenceExtensions.get_playback_range(seq)
|
|
479
|
-
start = ${params.playbackStart !== undefined ? params.playbackStart : 'current_range.get_start_frame()'}
|
|
480
|
-
end = ${params.playbackEnd !== undefined ? params.playbackEnd : 'current_range.get_end_frame()'}
|
|
481
|
-
# Use set_playback_start and set_playback_end instead
|
|
482
|
-
if ${params.playbackStart !== undefined}:
|
|
483
|
-
unreal.MovieSceneSequenceExtensions.set_playback_start(seq, ${params.playbackStart})
|
|
484
|
-
if ${params.playbackEnd !== undefined}:
|
|
485
|
-
unreal.MovieSceneSequenceExtensions.set_playback_end(seq, ${params.playbackEnd})
|
|
486
|
-
result['changes'].append({'property': 'playbackRange', 'start': start, 'end': end})
|
|
487
|
-
` : ''}
|
|
488
|
-
|
|
489
|
-
# Set total length in frames if provided
|
|
490
|
-
${params.lengthInFrames ? `
|
|
491
|
-
# This sets the playback end to match the desired length
|
|
492
|
-
start = unreal.MovieSceneSequenceExtensions.get_playback_start(seq)
|
|
493
|
-
end = start + ${params.lengthInFrames}
|
|
494
|
-
unreal.MovieSceneSequenceExtensions.set_playback_end(seq, end)
|
|
495
|
-
result['changes'].append({'property': 'lengthInFrames', 'value': ${params.lengthInFrames}})
|
|
496
|
-
` : ''}
|
|
497
|
-
|
|
498
|
-
# Get final properties for confirmation
|
|
499
|
-
final_rate = unreal.MovieSceneSequenceExtensions.get_display_rate(seq)
|
|
500
|
-
final_range = unreal.MovieSceneSequenceExtensions.get_playback_range(seq)
|
|
501
|
-
result['finalProperties'] = {
|
|
502
|
-
'frameRate': {'numerator': final_rate.numerator, 'denominator': final_rate.denominator},
|
|
503
|
-
'playbackStart': final_range.get_start_frame(),
|
|
504
|
-
'playbackEnd': final_range.get_end_frame(),
|
|
505
|
-
'duration': final_range.get_end_frame() - final_range.get_start_frame()
|
|
111
|
+
return resp;
|
|
112
|
+
}
|
|
113
|
+
async setDisplayRate(params) {
|
|
114
|
+
const resp = await this.sendAction('sequence_set_display_rate', { path: params.path, frameRate: params.frameRate });
|
|
115
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
116
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_display_rate' };
|
|
506
117
|
}
|
|
507
|
-
|
|
508
|
-
print('RESULT:' + json.dumps(result))
|
|
509
|
-
except Exception as e:
|
|
510
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
511
|
-
`.trim();
|
|
512
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'setSequenceProperties');
|
|
513
|
-
return this.parsePythonResult(resp, 'setSequenceProperties');
|
|
118
|
+
return resp;
|
|
514
119
|
}
|
|
515
|
-
/**
|
|
516
|
-
* Get sequence properties
|
|
517
|
-
*/
|
|
518
120
|
async getSequenceProperties(params) {
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
# Load the sequence
|
|
523
|
-
seq_path = r"${params.path || ''}"
|
|
524
|
-
if seq_path:
|
|
525
|
-
seq = unreal.load_asset(seq_path)
|
|
526
|
-
else:
|
|
527
|
-
# Try to get the currently open sequence
|
|
528
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
529
|
-
|
|
530
|
-
if not seq:
|
|
531
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence found or loaded'}))
|
|
532
|
-
else:
|
|
533
|
-
# Get all properties
|
|
534
|
-
display_rate = unreal.MovieSceneSequenceExtensions.get_display_rate(seq)
|
|
535
|
-
playback_range = unreal.MovieSceneSequenceExtensions.get_playback_range(seq)
|
|
536
|
-
|
|
537
|
-
# Get marked frames if any
|
|
538
|
-
marked_frames = []
|
|
539
|
-
try:
|
|
540
|
-
frames = unreal.MovieSceneSequenceExtensions.get_marked_frames(seq)
|
|
541
|
-
marked_frames = [{'frame': f.frame_number.value, 'label': f.label} for f in frames]
|
|
542
|
-
except:
|
|
543
|
-
pass
|
|
544
|
-
|
|
545
|
-
result = {
|
|
546
|
-
'success': True,
|
|
547
|
-
'path': seq.get_path_name(),
|
|
548
|
-
'name': seq.get_name(),
|
|
549
|
-
'frameRate': {
|
|
550
|
-
'numerator': display_rate.numerator,
|
|
551
|
-
'denominator': display_rate.denominator,
|
|
552
|
-
'fps': float(display_rate.numerator) / float(display_rate.denominator) if display_rate.denominator > 0 else 0
|
|
553
|
-
},
|
|
554
|
-
'playbackStart': playback_range.get_start_frame(),
|
|
555
|
-
'playbackEnd': playback_range.get_end_frame(),
|
|
556
|
-
'duration': playback_range.get_end_frame() - playback_range.get_start_frame(),
|
|
557
|
-
'markedFrames': marked_frames
|
|
121
|
+
const resp = await this.sendAction('sequence_get_properties', { path: params.path });
|
|
122
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
123
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_properties' };
|
|
558
124
|
}
|
|
559
|
-
|
|
560
|
-
print('RESULT:' + json.dumps(result))
|
|
561
|
-
except Exception as e:
|
|
562
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
563
|
-
`.trim();
|
|
564
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'getSequenceProperties');
|
|
565
|
-
return this.parsePythonResult(resp, 'getSequenceProperties');
|
|
125
|
+
return resp;
|
|
566
126
|
}
|
|
567
|
-
/**
|
|
568
|
-
* Set playback speed/rate
|
|
569
|
-
*/
|
|
570
127
|
async setPlaybackSpeed(params) {
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
578
|
-
`.trim();
|
|
579
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'setPlaybackSpeed');
|
|
580
|
-
return this.parsePythonResult(resp, 'setPlaybackSpeed');
|
|
128
|
+
const path = this.resolveSequencePath(params.path);
|
|
129
|
+
const resp = await this.sendAction('sequence_set_playback_speed', { path, speed: params.speed });
|
|
130
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
131
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_playback_speed' };
|
|
132
|
+
}
|
|
133
|
+
return resp;
|
|
581
134
|
}
|
|
582
|
-
/**
|
|
583
|
-
* Get all bindings in the current sequence
|
|
584
|
-
*/
|
|
585
135
|
async getBindings(params) {
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if seq_path:
|
|
592
|
-
seq = unreal.load_asset(seq_path)
|
|
593
|
-
else:
|
|
594
|
-
# Try to get the currently open sequence
|
|
595
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
596
|
-
|
|
597
|
-
if not seq:
|
|
598
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence found or loaded'}))
|
|
599
|
-
else:
|
|
600
|
-
bindings = unreal.MovieSceneSequenceExtensions.get_bindings(seq)
|
|
601
|
-
binding_list = []
|
|
602
|
-
for binding in bindings:
|
|
603
|
-
try:
|
|
604
|
-
binding_name = unreal.MovieSceneBindingExtensions.get_name(binding)
|
|
605
|
-
binding_guid = unreal.MovieSceneBindingExtensions.get_id(binding)
|
|
606
|
-
|
|
607
|
-
# Extract clean GUID string
|
|
608
|
-
guid_str = str(binding_guid)
|
|
609
|
-
if guid_str.startswith('<Guid '):
|
|
610
|
-
# Extract the actual GUID value from <Guid 'XXXX-XXXX-XXXX-XXXX'>
|
|
611
|
-
guid_clean = guid_str.replace('<Guid ', '').replace('>', '').replace("'", '').split(' ')[0]
|
|
612
|
-
else:
|
|
613
|
-
guid_clean = guid_str
|
|
614
|
-
|
|
615
|
-
binding_list.append({
|
|
616
|
-
'id': guid_clean,
|
|
617
|
-
'name': binding_name,
|
|
618
|
-
'guid': guid_clean
|
|
619
|
-
})
|
|
620
|
-
except:
|
|
621
|
-
pass
|
|
622
|
-
|
|
623
|
-
print('RESULT:' + json.dumps({'success': True, 'bindings': binding_list, 'count': len(binding_list)}))
|
|
624
|
-
except Exception as e:
|
|
625
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
626
|
-
`.trim();
|
|
627
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'getBindings');
|
|
628
|
-
return this.parsePythonResult(resp, 'getBindings');
|
|
136
|
+
const resp = await this.sendAction('sequence_get_bindings', { path: params?.path });
|
|
137
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
138
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_bindings' };
|
|
139
|
+
}
|
|
140
|
+
return resp;
|
|
629
141
|
}
|
|
630
|
-
/**
|
|
631
|
-
* Add multiple actors to sequence at once
|
|
632
|
-
*/
|
|
633
142
|
async addActors(params) {
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Subsystem unavailable'}))
|
|
641
|
-
else:
|
|
642
|
-
actor_names = ${JSON.stringify(params.actorNames)}
|
|
643
|
-
actors_to_add = []
|
|
644
|
-
not_found = []
|
|
645
|
-
|
|
646
|
-
all_actors = actor_sub.get_all_level_actors()
|
|
647
|
-
for name in actor_names:
|
|
648
|
-
found = False
|
|
649
|
-
for a in all_actors:
|
|
650
|
-
if not a: continue
|
|
651
|
-
label = a.get_actor_label()
|
|
652
|
-
actor_name = a.get_name()
|
|
653
|
-
if label == name or actor_name == name or label.startswith(name):
|
|
654
|
-
actors_to_add.append(a)
|
|
655
|
-
found = True
|
|
656
|
-
break
|
|
657
|
-
if not found:
|
|
658
|
-
not_found.append(name)
|
|
659
|
-
|
|
660
|
-
# Make sure we have a focused sequence
|
|
661
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
662
|
-
if not seq:
|
|
663
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence is currently focused'}))
|
|
664
|
-
elif len(actors_to_add) == 0:
|
|
665
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'No actors found: {not_found}'}))
|
|
666
|
-
else:
|
|
667
|
-
# Add all actors at once
|
|
668
|
-
bindings = ls.add_actors(actors_to_add)
|
|
669
|
-
added_actors = [a.get_actor_label() for a in actors_to_add]
|
|
670
|
-
print('RESULT:' + json.dumps({
|
|
671
|
-
'success': True,
|
|
672
|
-
'count': len(bindings) if bindings else len(actors_to_add),
|
|
673
|
-
'actorsAdded': added_actors,
|
|
674
|
-
'notFound': not_found
|
|
675
|
-
}))
|
|
676
|
-
except Exception as e:
|
|
677
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
678
|
-
`.trim();
|
|
679
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'addActors');
|
|
680
|
-
return this.parsePythonResult(resp, 'addActors');
|
|
143
|
+
const path = this.resolveSequencePath(params.path);
|
|
144
|
+
const resp = await this.sendAction('sequence_add_actors', { path, actorNames: params.actorNames });
|
|
145
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
146
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_actors' };
|
|
147
|
+
}
|
|
148
|
+
return resp;
|
|
681
149
|
}
|
|
682
|
-
/**
|
|
683
|
-
* Remove actors from binding
|
|
684
|
-
*/
|
|
685
150
|
async removeActors(params) {
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if not ls or not actor_sub:
|
|
693
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Subsystem unavailable'}))
|
|
694
|
-
else:
|
|
695
|
-
# Get current sequence
|
|
696
|
-
seq = unreal.LevelSequenceEditorBlueprintLibrary.get_focused_level_sequence()
|
|
697
|
-
if not seq:
|
|
698
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'No sequence is currently focused'}))
|
|
699
|
-
else:
|
|
700
|
-
actor_names = ${JSON.stringify(params.actorNames)}
|
|
701
|
-
actors_to_remove = []
|
|
702
|
-
|
|
703
|
-
all_actors = actor_sub.get_all_level_actors()
|
|
704
|
-
for name in actor_names:
|
|
705
|
-
for a in all_actors:
|
|
706
|
-
if not a: continue
|
|
707
|
-
label = a.get_actor_label()
|
|
708
|
-
actor_name = a.get_name()
|
|
709
|
-
if label == name or actor_name == name:
|
|
710
|
-
actors_to_remove.append(a)
|
|
711
|
-
break
|
|
712
|
-
|
|
713
|
-
# Get all bindings and remove matching actors
|
|
714
|
-
bindings = unreal.MovieSceneSequenceExtensions.get_bindings(seq)
|
|
715
|
-
removed_count = 0
|
|
716
|
-
for binding in bindings:
|
|
717
|
-
try:
|
|
718
|
-
ls.remove_actors_from_binding(actors_to_remove, binding)
|
|
719
|
-
removed_count += 1
|
|
720
|
-
except:
|
|
721
|
-
pass
|
|
722
|
-
|
|
723
|
-
print('RESULT:' + json.dumps({
|
|
724
|
-
'success': True,
|
|
725
|
-
'removedActors': [a.get_actor_label() for a in actors_to_remove],
|
|
726
|
-
'bindingsProcessed': removed_count
|
|
727
|
-
}))
|
|
728
|
-
except Exception as e:
|
|
729
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
730
|
-
`.trim();
|
|
731
|
-
const resp = await this.executeWithRetry(() => this.bridge.executePython(py), 'removeActors');
|
|
732
|
-
return this.parsePythonResult(resp, 'removeActors');
|
|
151
|
+
const path = this.resolveSequencePath(params.path);
|
|
152
|
+
const resp = await this.sendAction('sequence_remove_actors', { path, actorNames: params.actorNames });
|
|
153
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
154
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_remove_actors' };
|
|
155
|
+
}
|
|
156
|
+
return resp;
|
|
733
157
|
}
|
|
734
|
-
/**
|
|
735
|
-
* Create a spawnable from an actor class
|
|
736
|
-
*/
|
|
737
158
|
async addSpawnableFromClass(params) {
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
159
|
+
const resp = await this.sendAction('sequence_add_spawnable_from_class', { className: params.className, path: params.path });
|
|
160
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
161
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_spawnable_from_class' };
|
|
162
|
+
}
|
|
163
|
+
return resp;
|
|
164
|
+
}
|
|
165
|
+
async list(params) {
|
|
166
|
+
const resp = await this.sendAction('sequence_list', { path: params?.path });
|
|
167
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
168
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_list' };
|
|
169
|
+
}
|
|
170
|
+
if (resp.success) {
|
|
171
|
+
const sequences = resp.sequences || resp.data || resp.result || [];
|
|
172
|
+
return {
|
|
173
|
+
...resp,
|
|
174
|
+
sequences,
|
|
175
|
+
count: Array.isArray(sequences) ? sequences.length : undefined
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return resp;
|
|
179
|
+
}
|
|
180
|
+
async duplicate(params) {
|
|
181
|
+
const resp = await this.sendAction('sequence_duplicate', { path: params.path, destinationPath: params.destinationPath });
|
|
182
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
183
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_duplicate' };
|
|
184
|
+
}
|
|
185
|
+
return resp;
|
|
186
|
+
}
|
|
187
|
+
async rename(params) {
|
|
188
|
+
const resp = await this.sendAction('sequence_rename', { path: params.path, newName: params.newName });
|
|
189
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
190
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_rename' };
|
|
191
|
+
}
|
|
192
|
+
return resp;
|
|
193
|
+
}
|
|
194
|
+
async deleteSequence(params) {
|
|
195
|
+
const resp = await this.sendAction('sequence_delete', { path: params.path });
|
|
196
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
197
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_delete' };
|
|
198
|
+
}
|
|
199
|
+
return resp;
|
|
200
|
+
}
|
|
201
|
+
async getMetadata(params) {
|
|
202
|
+
const resp = await this.sendAction('sequence_get_metadata', { path: params.path });
|
|
203
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
204
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_metadata' };
|
|
205
|
+
}
|
|
206
|
+
return resp;
|
|
207
|
+
}
|
|
208
|
+
async addKeyframe(params) {
|
|
209
|
+
const resp = await this.sendAction('sequence_add_keyframe', {
|
|
210
|
+
path: params.path,
|
|
211
|
+
bindingId: params.bindingId,
|
|
212
|
+
actorName: params.actorName,
|
|
213
|
+
property: params.property,
|
|
214
|
+
frame: params.frame,
|
|
215
|
+
value: params.value
|
|
216
|
+
});
|
|
217
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
218
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_keyframe' };
|
|
219
|
+
}
|
|
220
|
+
return resp;
|
|
221
|
+
}
|
|
222
|
+
async listTracks(params) {
|
|
223
|
+
const resp = await this.sendAction('sequence_list_tracks', { path: params.path });
|
|
224
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
225
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_list_tracks' };
|
|
226
|
+
}
|
|
227
|
+
if (resp.success) {
|
|
228
|
+
const tracks = resp.tracks || resp.data || resp.result || [];
|
|
229
|
+
return {
|
|
230
|
+
...resp,
|
|
231
|
+
tracks,
|
|
232
|
+
count: Array.isArray(tracks) ? tracks.length : undefined
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return resp;
|
|
236
|
+
}
|
|
237
|
+
async setWorkRange(params) {
|
|
238
|
+
const resp = await this.sendAction('sequence_set_work_range', { path: params.path, start: params.start, end: params.end });
|
|
239
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
240
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_work_range' };
|
|
241
|
+
}
|
|
242
|
+
return resp;
|
|
795
243
|
}
|
|
796
244
|
}
|
|
797
245
|
//# sourceMappingURL=sequence.js.map
|