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/sequence.js
CHANGED
|
@@ -1,797 +1,258 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export class SequenceTools {
|
|
4
|
-
bridge;
|
|
5
|
-
log = new Logger('SequenceTools');
|
|
1
|
+
import { BaseTool } from './base-tool.js';
|
|
2
|
+
import { wasmIntegration } from '../wasm/index.js';
|
|
3
|
+
export class SequenceTools extends BaseTool {
|
|
6
4
|
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
|
-
}
|
|
5
|
+
activeSequencePath;
|
|
6
|
+
resolveSequencePath(explicitPath) {
|
|
7
|
+
if (typeof explicitPath === 'string' && explicitPath.trim().length > 0) {
|
|
8
|
+
return explicitPath.trim();
|
|
32
9
|
}
|
|
33
|
-
|
|
10
|
+
return this.activeSequencePath;
|
|
34
11
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
...interpreted.payload,
|
|
46
|
-
success: true
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const baseError = interpreted.error ?? `${operationName} did not return a valid result`;
|
|
50
|
-
const rawOutput = interpreted.rawText ?? '';
|
|
51
|
-
const cleanedOutput = interpreted.cleanText && interpreted.cleanText.trim().length > 0
|
|
52
|
-
? interpreted.cleanText.trim()
|
|
53
|
-
: baseError;
|
|
54
|
-
if (rawOutput.includes('ModuleNotFoundError')) {
|
|
55
|
-
return { success: false, error: 'Sequencer module not available. Ensure Sequencer is enabled.' };
|
|
12
|
+
async sendAction(action, payload = {}, timeoutMs) {
|
|
13
|
+
const envDefault = Number(process.env.MCP_AUTOMATION_REQUEST_TIMEOUT_MS ?? '120000');
|
|
14
|
+
const defaultTimeout = Number.isFinite(envDefault) && envDefault > 0 ? envDefault : 120000;
|
|
15
|
+
const finalTimeout = typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : defaultTimeout;
|
|
16
|
+
try {
|
|
17
|
+
const response = await this.sendAutomationRequest(action, payload, { timeoutMs: finalTimeout, waitForEvent: false });
|
|
18
|
+
const success = response && response.success !== false;
|
|
19
|
+
const result = response.result ?? response;
|
|
20
|
+
return { success, message: response.message ?? undefined, error: response.success === false ? (response.error ?? response.message) : undefined, result, requestId: response.requestId };
|
|
56
21
|
}
|
|
57
|
-
|
|
58
|
-
return { success: false, error:
|
|
22
|
+
catch (err) {
|
|
23
|
+
return { success: false, error: String(err), message: String(err) };
|
|
59
24
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
: (cleanedOutput ?? '').substring(0, 200).trim();
|
|
67
|
-
return detail ? `${baseError}: ${detail}` : baseError;
|
|
68
|
-
})()
|
|
69
|
-
};
|
|
25
|
+
}
|
|
26
|
+
isUnknownActionResponse(res) {
|
|
27
|
+
if (!res)
|
|
28
|
+
return false;
|
|
29
|
+
const txt = String((res.error ?? res.message ?? '')).toLowerCase();
|
|
30
|
+
return txt.includes('unknown_action') || txt.includes('unknown automation action') || txt.includes('not_implemented') || txt === 'unknown_plugin_action';
|
|
70
31
|
}
|
|
71
32
|
async create(params) {
|
|
72
33
|
const name = params.name?.trim();
|
|
73
34
|
const base = (params.path || '/Game/Sequences').replace(/\/$/, '');
|
|
74
35
|
if (!name)
|
|
75
36
|
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
|
-
};
|
|
37
|
+
const payload = { name, path: base };
|
|
38
|
+
const resp = await this.sendAction('sequence_create', payload, params.timeoutMs);
|
|
39
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
40
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_create' };
|
|
87
41
|
}
|
|
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
|
-
};
|
|
42
|
+
if (resp.success && resp.result && resp.result.sequencePath) {
|
|
43
|
+
const sequence = { path: resp.result.sequencePath, name };
|
|
123
44
|
this.sequenceCache.set(sequence.path, sequence);
|
|
45
|
+
return { ...resp, sequence: resp.result.sequencePath };
|
|
124
46
|
}
|
|
125
|
-
return
|
|
47
|
+
return resp;
|
|
126
48
|
}
|
|
127
49
|
async open(params) {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
50
|
+
const path = params.path?.trim();
|
|
51
|
+
const resp = await this.sendAction('sequence_open', { path });
|
|
52
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
53
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_open' };
|
|
54
|
+
}
|
|
55
|
+
if (resp && resp.success !== false && path) {
|
|
56
|
+
this.activeSequencePath = path;
|
|
134
57
|
}
|
|
135
|
-
|
|
136
|
-
import unreal, json
|
|
137
|
-
path = r"${params.path}"
|
|
138
|
-
try:
|
|
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');
|
|
58
|
+
return resp;
|
|
150
59
|
}
|
|
151
60
|
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
|
-
};
|
|
61
|
+
const path = this.resolveSequencePath(params.path);
|
|
62
|
+
const resp = await this.sendAction('sequence_add_camera', { path, spawnable: params.spawnable !== false });
|
|
63
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
64
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_camera' };
|
|
162
65
|
}
|
|
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');
|
|
66
|
+
return resp;
|
|
231
67
|
}
|
|
232
68
|
async addActor(params) {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
238
|
-
};
|
|
69
|
+
const path = this.resolveSequencePath(params.path);
|
|
70
|
+
const resp = await this.sendAction('sequence_add_actor', { path, actorName: params.actorName, createBinding: params.createBinding });
|
|
71
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
72
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_actor' };
|
|
239
73
|
}
|
|
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');
|
|
74
|
+
return resp;
|
|
326
75
|
}
|
|
327
|
-
/**
|
|
328
|
-
* Play the current level sequence
|
|
329
|
-
*/
|
|
330
76
|
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
|
-
};
|
|
77
|
+
const path = this.resolveSequencePath(params?.path);
|
|
78
|
+
const resp = await this.sendAction('sequence_play', { path, startTime: params?.startTime, loopMode: params?.loopMode });
|
|
79
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
80
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_play' };
|
|
343
81
|
}
|
|
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');
|
|
82
|
+
return resp;
|
|
373
83
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (missingPlugins) {
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
383
|
-
};
|
|
84
|
+
async pause(params) {
|
|
85
|
+
const path = this.resolveSequencePath(params?.path);
|
|
86
|
+
const resp = await this.sendAction('sequence_pause', { path });
|
|
87
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
88
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_pause' };
|
|
384
89
|
}
|
|
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');
|
|
90
|
+
return resp;
|
|
395
91
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (missingPlugins) {
|
|
402
|
-
return {
|
|
403
|
-
success: false,
|
|
404
|
-
error: `Required Unreal plugins are not enabled: ${missingPlugins.join(', ')}`
|
|
405
|
-
};
|
|
92
|
+
async stop(params) {
|
|
93
|
+
const path = this.resolveSequencePath(params?.path);
|
|
94
|
+
const resp = await this.sendAction('sequence_stop', { path });
|
|
95
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
96
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_stop' };
|
|
406
97
|
}
|
|
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');
|
|
98
|
+
return resp;
|
|
417
99
|
}
|
|
418
|
-
/**
|
|
419
|
-
* Set sequence properties including frame rate and length
|
|
420
|
-
*/
|
|
421
100
|
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
|
-
};
|
|
101
|
+
const payload = {
|
|
102
|
+
path: params.path,
|
|
103
|
+
frameRate: params.frameRate,
|
|
104
|
+
lengthInFrames: params.lengthInFrames,
|
|
105
|
+
playbackStart: params.playbackStart,
|
|
106
|
+
playbackEnd: params.playbackEnd
|
|
107
|
+
};
|
|
108
|
+
const resp = await this.sendAction('sequence_set_properties', payload);
|
|
109
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
110
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_properties' };
|
|
452
111
|
}
|
|
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()
|
|
112
|
+
return resp;
|
|
113
|
+
}
|
|
114
|
+
async setDisplayRate(params) {
|
|
115
|
+
const resp = await this.sendAction('sequence_set_display_rate', { path: params.path, frameRate: params.frameRate });
|
|
116
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
117
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_display_rate' };
|
|
506
118
|
}
|
|
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');
|
|
119
|
+
return resp;
|
|
514
120
|
}
|
|
515
|
-
/**
|
|
516
|
-
* Get sequence properties
|
|
517
|
-
*/
|
|
518
121
|
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
|
|
122
|
+
const resp = await this.sendAction('sequence_get_properties', { path: params.path });
|
|
123
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
124
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_properties' };
|
|
558
125
|
}
|
|
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');
|
|
126
|
+
return resp;
|
|
566
127
|
}
|
|
567
|
-
/**
|
|
568
|
-
* Set playback speed/rate
|
|
569
|
-
*/
|
|
570
128
|
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');
|
|
129
|
+
const path = this.resolveSequencePath(params.path);
|
|
130
|
+
const resp = await this.sendAction('sequence_set_playback_speed', { path, speed: params.speed });
|
|
131
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
132
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_playback_speed' };
|
|
133
|
+
}
|
|
134
|
+
return resp;
|
|
581
135
|
}
|
|
582
|
-
/**
|
|
583
|
-
* Get all bindings in the current sequence
|
|
584
|
-
*/
|
|
585
136
|
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');
|
|
137
|
+
const resp = await this.sendAction('sequence_get_bindings', { path: params?.path });
|
|
138
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
139
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_bindings' };
|
|
140
|
+
}
|
|
141
|
+
return resp;
|
|
629
142
|
}
|
|
630
|
-
/**
|
|
631
|
-
* Add multiple actors to sequence at once
|
|
632
|
-
*/
|
|
633
143
|
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');
|
|
144
|
+
const path = this.resolveSequencePath(params.path);
|
|
145
|
+
const resp = await this.sendAction('sequence_add_actors', { path, actorNames: params.actorNames });
|
|
146
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
147
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_actors' };
|
|
148
|
+
}
|
|
149
|
+
return resp;
|
|
681
150
|
}
|
|
682
|
-
/**
|
|
683
|
-
* Remove actors from binding
|
|
684
|
-
*/
|
|
685
151
|
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');
|
|
152
|
+
const path = this.resolveSequencePath(params.path);
|
|
153
|
+
const resp = await this.sendAction('sequence_remove_actors', { path, actorNames: params.actorNames });
|
|
154
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
155
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_remove_actors' };
|
|
156
|
+
}
|
|
157
|
+
return resp;
|
|
733
158
|
}
|
|
734
|
-
/**
|
|
735
|
-
* Create a spawnable from an actor class
|
|
736
|
-
*/
|
|
737
159
|
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
|
-
|
|
160
|
+
const resp = await this.sendAction('sequence_add_spawnable_from_class', { className: params.className, path: params.path });
|
|
161
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
162
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_spawnable_from_class' };
|
|
163
|
+
}
|
|
164
|
+
return resp;
|
|
165
|
+
}
|
|
166
|
+
async list(params) {
|
|
167
|
+
const resp = await this.sendAction('sequence_list', { path: params?.path });
|
|
168
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
169
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_list' };
|
|
170
|
+
}
|
|
171
|
+
if (resp.success) {
|
|
172
|
+
const sequences = resp.sequences || resp.data || resp.result || [];
|
|
173
|
+
return {
|
|
174
|
+
...resp,
|
|
175
|
+
sequences,
|
|
176
|
+
count: Array.isArray(sequences) ? sequences.length : undefined
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return resp;
|
|
180
|
+
}
|
|
181
|
+
async duplicate(params) {
|
|
182
|
+
const resp = await this.sendAction('sequence_duplicate', { path: params.path, destinationPath: params.destinationPath });
|
|
183
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
184
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_duplicate' };
|
|
185
|
+
}
|
|
186
|
+
return resp;
|
|
187
|
+
}
|
|
188
|
+
async rename(params) {
|
|
189
|
+
const resp = await this.sendAction('sequence_rename', { path: params.path, newName: params.newName });
|
|
190
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
191
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_rename' };
|
|
192
|
+
}
|
|
193
|
+
return resp;
|
|
194
|
+
}
|
|
195
|
+
async deleteSequence(params) {
|
|
196
|
+
const resp = await this.sendAction('sequence_delete', { path: params.path });
|
|
197
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
198
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_delete' };
|
|
199
|
+
}
|
|
200
|
+
return resp;
|
|
201
|
+
}
|
|
202
|
+
async getMetadata(params) {
|
|
203
|
+
const resp = await this.sendAction('sequence_get_metadata', { path: params.path });
|
|
204
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
205
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_get_metadata' };
|
|
206
|
+
}
|
|
207
|
+
return resp;
|
|
208
|
+
}
|
|
209
|
+
async addKeyframe(params) {
|
|
210
|
+
const resp = await this.sendAction('sequence_add_keyframe', {
|
|
211
|
+
path: params.path,
|
|
212
|
+
bindingId: params.bindingId,
|
|
213
|
+
actorName: params.actorName,
|
|
214
|
+
property: params.property,
|
|
215
|
+
frame: params.frame,
|
|
216
|
+
value: params.value
|
|
217
|
+
});
|
|
218
|
+
if (params.property === 'Transform' && params.value) {
|
|
219
|
+
const loc = params.value.location;
|
|
220
|
+
const rot = params.value.rotation;
|
|
221
|
+
const scale = params.value.scale;
|
|
222
|
+
if (loc && rot && scale) {
|
|
223
|
+
const locArr = [loc.x, loc.y, loc.z];
|
|
224
|
+
const rotArr = [rot.pitch, rot.yaw, rot.roll];
|
|
225
|
+
const scaleArr = [scale.x, scale.y, scale.z];
|
|
226
|
+
wasmIntegration.composeTransform(locArr, rotArr, scaleArr);
|
|
227
|
+
console.error('[WASM] Using composeTransform for keyframe validation');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
231
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_add_keyframe' };
|
|
232
|
+
}
|
|
233
|
+
return resp;
|
|
234
|
+
}
|
|
235
|
+
async listTracks(params) {
|
|
236
|
+
const resp = await this.sendAction('sequence_list_tracks', { path: params.path });
|
|
237
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
238
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_list_tracks' };
|
|
239
|
+
}
|
|
240
|
+
if (resp.success) {
|
|
241
|
+
const tracks = resp.tracks || resp.data || resp.result || [];
|
|
242
|
+
return {
|
|
243
|
+
...resp,
|
|
244
|
+
tracks,
|
|
245
|
+
count: Array.isArray(tracks) ? tracks.length : undefined
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return resp;
|
|
249
|
+
}
|
|
250
|
+
async setWorkRange(params) {
|
|
251
|
+
const resp = await this.sendAction('sequence_set_work_range', { path: params.path, start: params.start, end: params.end });
|
|
252
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
253
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement sequence_set_work_range' };
|
|
254
|
+
}
|
|
255
|
+
return resp;
|
|
795
256
|
}
|
|
796
257
|
}
|
|
797
258
|
//# sourceMappingURL=sequence.js.map
|