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/animation.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { cleanObject } from '../utils/safe-json.js';
|
|
1
2
|
import { validateAssetParams } from '../utils/validation.js';
|
|
2
|
-
import { interpretStandardResult, coerceBoolean, coerceString, coerceStringArray } from '../utils/result-helpers.js';
|
|
3
3
|
export class AnimationTools {
|
|
4
4
|
bridge;
|
|
5
|
-
|
|
5
|
+
managedArtifacts = new Map();
|
|
6
|
+
automationBridge;
|
|
7
|
+
constructor(bridge, automationBridge) {
|
|
6
8
|
this.bridge = bridge;
|
|
9
|
+
this.automationBridge = automationBridge;
|
|
10
|
+
}
|
|
11
|
+
setAutomationBridge(automationBridge) {
|
|
12
|
+
this.automationBridge = automationBridge;
|
|
13
|
+
}
|
|
14
|
+
trackArtifact(key, info) {
|
|
15
|
+
this.managedArtifacts.set(key, { ...info, createdAt: Date.now() });
|
|
7
16
|
}
|
|
8
17
|
async createAnimationBlueprint(params) {
|
|
9
18
|
try {
|
|
@@ -16,13 +25,63 @@ export class AnimationTools {
|
|
|
16
25
|
const sanitized = validation.sanitized;
|
|
17
26
|
const assetName = sanitized.name;
|
|
18
27
|
const assetPath = sanitized.savePath ?? targetPath;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const fullPath = `${assetPath}/${assetName}`;
|
|
29
|
+
if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
|
|
30
|
+
try {
|
|
31
|
+
const resp = await this.automationBridge.sendAutomationRequest('create_animation_blueprint', {
|
|
32
|
+
name: assetName,
|
|
33
|
+
skeletonPath: params.skeletonPath,
|
|
34
|
+
savePath: assetPath
|
|
35
|
+
}, { timeoutMs: 60000 });
|
|
36
|
+
const result = resp?.result ?? resp;
|
|
37
|
+
const resultObj = result && typeof result === 'object' ? result : undefined;
|
|
38
|
+
const warnings = Array.isArray(resultObj?.warnings) ? resultObj.warnings : undefined;
|
|
39
|
+
const details = Array.isArray(resultObj?.details) ? resultObj.details : undefined;
|
|
40
|
+
const isSuccess = resp && resp.success !== false && !!resultObj;
|
|
41
|
+
if (isSuccess && resultObj) {
|
|
42
|
+
const blueprintPath = typeof resultObj.blueprintPath === 'string' ? resultObj.blueprintPath : fullPath;
|
|
43
|
+
this.trackArtifact(assetName, { path: blueprintPath, type: 'AnimationBlueprint' });
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
message: resp.message || `Animation Blueprint created at ${blueprintPath}`,
|
|
47
|
+
path: blueprintPath,
|
|
48
|
+
skeleton: params.skeletonPath,
|
|
49
|
+
warnings,
|
|
50
|
+
details
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const message = typeof resp?.message === 'string'
|
|
54
|
+
? resp.message
|
|
55
|
+
: (typeof resp?.error === 'string' ? resp.error : 'Animation Blueprint creation failed');
|
|
56
|
+
const error = typeof resp?.error === 'string' ? resp.error : message;
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
message,
|
|
60
|
+
error,
|
|
61
|
+
path: fullPath,
|
|
62
|
+
skeleton: params.skeletonPath,
|
|
63
|
+
warnings,
|
|
64
|
+
details
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const error = String(err);
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
message: `Failed to create Animation Blueprint: ${error}`,
|
|
72
|
+
error,
|
|
73
|
+
path: fullPath,
|
|
74
|
+
skeleton: params.skeletonPath
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
message: 'Automation bridge not connected for Animation Blueprint creation',
|
|
81
|
+
error: 'AUTOMATION_BRIDGE_UNAVAILABLE',
|
|
82
|
+
path: fullPath,
|
|
83
|
+
skeleton: params.skeletonPath
|
|
84
|
+
};
|
|
26
85
|
}
|
|
27
86
|
catch (err) {
|
|
28
87
|
const error = `Failed to create Animation Blueprint: ${err}`;
|
|
@@ -65,6 +124,72 @@ export class AnimationTools {
|
|
|
65
124
|
return { success: false, error: `Failed to add state machine: ${err}` };
|
|
66
125
|
}
|
|
67
126
|
}
|
|
127
|
+
async createStateMachine(params) {
|
|
128
|
+
try {
|
|
129
|
+
const rawName = typeof params.machineName === 'string' ? params.machineName.trim() : '';
|
|
130
|
+
const machineName = rawName || 'StateMachine';
|
|
131
|
+
const normalizedStates = Array.isArray(params.states)
|
|
132
|
+
? params.states
|
|
133
|
+
.map((s) => {
|
|
134
|
+
if (typeof s === 'string') {
|
|
135
|
+
const name = s.trim();
|
|
136
|
+
return name ? { name } : undefined;
|
|
137
|
+
}
|
|
138
|
+
if (s && typeof s === 'object' && typeof s.name === 'string') {
|
|
139
|
+
const name = s.name.trim();
|
|
140
|
+
if (!name)
|
|
141
|
+
return undefined;
|
|
142
|
+
return s;
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
})
|
|
146
|
+
.filter((s) => !!s)
|
|
147
|
+
: [];
|
|
148
|
+
const normalizedTransitionsRaw = Array.isArray(params.transitions)
|
|
149
|
+
? params.transitions
|
|
150
|
+
.map((t) => {
|
|
151
|
+
if (!t || typeof t !== 'object')
|
|
152
|
+
return undefined;
|
|
153
|
+
const src = (t.sourceState || '').trim();
|
|
154
|
+
const dst = (t.targetState || '').trim();
|
|
155
|
+
if (!src || !dst)
|
|
156
|
+
return undefined;
|
|
157
|
+
return { sourceState: src, targetState: dst, condition: t.condition };
|
|
158
|
+
})
|
|
159
|
+
.filter((t) => !!t)
|
|
160
|
+
: [];
|
|
161
|
+
const normalizedTransitions = normalizedTransitionsRaw;
|
|
162
|
+
const blueprintPath = typeof params.blueprintPath === 'string' && params.blueprintPath.trim().length > 0
|
|
163
|
+
? params.blueprintPath.trim()
|
|
164
|
+
: undefined;
|
|
165
|
+
const key = `StateMachine:${machineName}`;
|
|
166
|
+
this.trackArtifact(key, {
|
|
167
|
+
path: blueprintPath,
|
|
168
|
+
type: 'AnimationStateMachine',
|
|
169
|
+
metadata: {
|
|
170
|
+
machineName,
|
|
171
|
+
states: normalizedStates,
|
|
172
|
+
transitions: normalizedTransitions
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
success: true,
|
|
177
|
+
message: `State machine '${machineName}' specification recorded`,
|
|
178
|
+
machineName,
|
|
179
|
+
blueprintPath,
|
|
180
|
+
states: normalizedStates.length ? normalizedStates : undefined,
|
|
181
|
+
transitions: normalizedTransitions.length ? normalizedTransitions : undefined
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const error = String(err);
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
message: `Failed to record state machine specification: ${error}`,
|
|
189
|
+
error
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
68
193
|
async createBlendSpace(params) {
|
|
69
194
|
try {
|
|
70
195
|
const targetPath = params.savePath ?? '/Game/Animations';
|
|
@@ -76,69 +201,401 @@ export class AnimationTools {
|
|
|
76
201
|
const assetName = sanitized.name;
|
|
77
202
|
const assetPath = sanitized.savePath ?? targetPath;
|
|
78
203
|
const dimensions = params.dimensions === 2 ? 2 : 1;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
204
|
+
if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
|
|
205
|
+
try {
|
|
206
|
+
const payload = {
|
|
207
|
+
action: 'create_blend_space',
|
|
208
|
+
name: assetName,
|
|
209
|
+
savePath: assetPath,
|
|
210
|
+
skeletonPath: params.skeletonPath,
|
|
211
|
+
dimensions,
|
|
212
|
+
samples: params.samples
|
|
213
|
+
};
|
|
214
|
+
if (params.horizontalAxis) {
|
|
215
|
+
payload.minX = params.horizontalAxis.minValue;
|
|
216
|
+
payload.maxX = params.horizontalAxis.maxValue;
|
|
217
|
+
}
|
|
218
|
+
if (params.verticalAxis) {
|
|
219
|
+
payload.minY = params.verticalAxis.minValue;
|
|
220
|
+
payload.maxY = params.verticalAxis.maxValue;
|
|
221
|
+
}
|
|
222
|
+
const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload));
|
|
223
|
+
const result = resp?.result ?? resp;
|
|
224
|
+
const resultObj = result && typeof result === 'object' ? result : undefined;
|
|
225
|
+
const isSuccess = resp && resp.success !== false && !!resultObj;
|
|
226
|
+
if (isSuccess && resultObj) {
|
|
227
|
+
const path = typeof resultObj.blendSpacePath === 'string'
|
|
228
|
+
? resultObj.blendSpacePath
|
|
229
|
+
: `${assetPath}/${assetName}`;
|
|
230
|
+
const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
|
|
231
|
+
const details = resultObj ? resultObj.details : undefined;
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
message: resp.message || `Blend Space ${assetName} created`,
|
|
235
|
+
path,
|
|
236
|
+
skeletonPath: params.skeletonPath,
|
|
237
|
+
details,
|
|
238
|
+
warnings
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const message = typeof resp?.message === 'string'
|
|
242
|
+
? resp.message
|
|
243
|
+
: (typeof resp?.error === 'string' ? resp.error : 'Blend space creation failed');
|
|
244
|
+
const error = typeof resp?.error === 'string' ? resp.error : message;
|
|
245
|
+
return { success: false, error };
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
const error = String(err);
|
|
249
|
+
return { success: false, error: `Failed to create blend space: ${error}` };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return { success: false, error: 'Automation bridge not connected for createBlendSpace' };
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
return { success: false, error: `Failed to create blend space: ${err}` };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async setupControlRig(params) {
|
|
259
|
+
try {
|
|
260
|
+
const targetPath = params.savePath ?? '/Game/Animations';
|
|
261
|
+
const validation = validateAssetParams({ name: params.name, savePath: targetPath });
|
|
262
|
+
if (!validation.valid) {
|
|
263
|
+
return { success: false, error: validation.error ?? 'Invalid asset parameters' };
|
|
264
|
+
}
|
|
265
|
+
const sanitized = validation.sanitized;
|
|
266
|
+
const assetName = sanitized.name;
|
|
267
|
+
const assetPath = sanitized.savePath ?? targetPath;
|
|
268
|
+
const fullPath = `${assetPath}/${assetName}`;
|
|
269
|
+
if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
|
|
270
|
+
try {
|
|
271
|
+
const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
|
|
272
|
+
action: 'setup_ik',
|
|
273
|
+
name: assetName,
|
|
274
|
+
savePath: assetPath,
|
|
275
|
+
skeletonPath: params.skeletonPath,
|
|
276
|
+
controls: params.controls
|
|
277
|
+
}), { timeoutMs: 60000 });
|
|
278
|
+
const result = resp?.result ?? resp;
|
|
279
|
+
const resultObj = result && typeof result === 'object' ? result : undefined;
|
|
280
|
+
const isSuccess = resp && resp.success !== false && !!resultObj;
|
|
281
|
+
if (isSuccess && resultObj) {
|
|
282
|
+
const controlRigPath = typeof resultObj.controlRigPath === 'string'
|
|
283
|
+
? resultObj.controlRigPath
|
|
284
|
+
: fullPath;
|
|
285
|
+
const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
|
|
286
|
+
const details = resultObj ? resultObj.details : undefined;
|
|
287
|
+
this.trackArtifact(assetName, { path: controlRigPath, type: 'ControlRig' });
|
|
288
|
+
return {
|
|
289
|
+
success: true,
|
|
290
|
+
message: resp.message || `Control Rig ${assetName} created`,
|
|
291
|
+
path: controlRigPath,
|
|
292
|
+
warnings,
|
|
293
|
+
details
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const message = typeof resp?.message === 'string'
|
|
297
|
+
? resp.message
|
|
298
|
+
: (typeof resp?.error === 'string' ? resp.error : 'Control Rig setup failed');
|
|
299
|
+
const error = typeof resp?.error === 'string' ? resp.error : message;
|
|
300
|
+
return { success: false, error };
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
const error = String(err);
|
|
304
|
+
return { success: false, error: `Failed to setup control rig: ${error}` };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return { success: false, error: 'Automation bridge not connected for setupControlRig' };
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
return { success: false, error: `Failed to setup control rig: ${err}` };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async setupIK(params) {
|
|
314
|
+
try {
|
|
315
|
+
const actorName = (params.actorName || 'Character').trim();
|
|
316
|
+
const ikBones = Array.isArray(params.ikBones)
|
|
317
|
+
? params.ikBones.map((b) => String(b)).filter((b) => b.trim().length > 0)
|
|
318
|
+
: [];
|
|
319
|
+
const key = `IK:${actorName}`;
|
|
320
|
+
this.trackArtifact(key, {
|
|
321
|
+
type: 'IKSetup',
|
|
322
|
+
metadata: {
|
|
323
|
+
actorName,
|
|
324
|
+
ikBones,
|
|
325
|
+
enableFootPlacement: params.enableFootPlacement === true
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
return {
|
|
329
|
+
success: true,
|
|
330
|
+
message: `IK setup specification recorded for actor '${actorName}'`,
|
|
331
|
+
actorName,
|
|
332
|
+
ikBones: ikBones.length ? ikBones : undefined,
|
|
333
|
+
enableFootPlacement: params.enableFootPlacement === true ? true : undefined
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
const error = String(err);
|
|
338
|
+
return {
|
|
339
|
+
success: false,
|
|
340
|
+
message: `Failed to record IK setup: ${error}`,
|
|
341
|
+
error
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async createProceduralAnim(params) {
|
|
346
|
+
try {
|
|
347
|
+
const baseName = (params.systemName || '').trim()
|
|
348
|
+
|| (params.baseAnimation ? params.baseAnimation.split('/').pop() || '' : '');
|
|
349
|
+
const systemName = baseName || 'ProceduralSystem';
|
|
350
|
+
const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
|
|
351
|
+
const path = `${basePath || '/Game/Animations'}/${systemName}`;
|
|
352
|
+
this.trackArtifact(systemName, {
|
|
353
|
+
path,
|
|
354
|
+
type: 'ProceduralAnimation',
|
|
355
|
+
metadata: {
|
|
356
|
+
baseAnimation: params.baseAnimation,
|
|
357
|
+
modifiers: Array.isArray(params.modifiers) ? params.modifiers : []
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
success: true,
|
|
362
|
+
message: `Procedural animation system '${systemName}' specification recorded at ${path}`,
|
|
363
|
+
path,
|
|
364
|
+
systemName
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
const error = String(err);
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
message: `Failed to record procedural animation system: ${error}`,
|
|
372
|
+
error
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async createBlendTree(params) {
|
|
377
|
+
try {
|
|
378
|
+
const rawName = (params.treeName || '').trim();
|
|
379
|
+
const treeName = rawName || 'BlendTree';
|
|
380
|
+
const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
|
|
381
|
+
const path = `${basePath || '/Game/Animations'}/${treeName}`;
|
|
382
|
+
this.trackArtifact(treeName, {
|
|
383
|
+
path,
|
|
384
|
+
type: 'BlendTree',
|
|
385
|
+
metadata: {
|
|
386
|
+
blendType: params.blendType,
|
|
387
|
+
basePose: params.basePose,
|
|
388
|
+
additiveAnimations: Array.isArray(params.additiveAnimations) ? params.additiveAnimations : []
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
return {
|
|
392
|
+
success: true,
|
|
393
|
+
message: `Blend tree '${treeName}' specification recorded at ${path}`,
|
|
394
|
+
path,
|
|
395
|
+
treeName
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
catch (err) {
|
|
399
|
+
const error = String(err);
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
message: `Failed to record blend tree specification: ${error}`,
|
|
403
|
+
error
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async cleanup(artifacts) {
|
|
408
|
+
try {
|
|
409
|
+
const pathsToDelete = [];
|
|
410
|
+
if (Array.isArray(artifacts) && artifacts.length > 0) {
|
|
411
|
+
pathsToDelete.push(...artifacts.map((a) => String(a).trim()).filter((a) => a.length > 0));
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
for (const [key, val] of this.managedArtifacts.entries()) {
|
|
415
|
+
if (val.path)
|
|
416
|
+
pathsToDelete.push(val.path);
|
|
417
|
+
else
|
|
418
|
+
pathsToDelete.push(key);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (pathsToDelete.length === 0) {
|
|
422
|
+
return {
|
|
423
|
+
success: true,
|
|
424
|
+
message: 'No artifacts to cleanup.'
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
let bridgeMessage = '';
|
|
428
|
+
if (this.automationBridge) {
|
|
429
|
+
try {
|
|
430
|
+
const response = await this.automationBridge.sendAutomationRequest('animation_physics', {
|
|
431
|
+
action: 'cleanup',
|
|
432
|
+
artifacts: pathsToDelete
|
|
433
|
+
});
|
|
434
|
+
if (!response.success) {
|
|
435
|
+
bridgeMessage = ` (Engine cleanup failed: ${response.message})`;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
bridgeMessage = ' (Engine assets deleted)';
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
catch (e) {
|
|
442
|
+
bridgeMessage = ` (Engine connection failed: ${e})`;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
bridgeMessage = ' (No automation bridge available)';
|
|
86
447
|
}
|
|
87
|
-
|
|
88
|
-
|
|
448
|
+
const removed = [];
|
|
449
|
+
const toRemoveKeys = [];
|
|
450
|
+
for (const [key, val] of this.managedArtifacts.entries()) {
|
|
451
|
+
if (pathsToDelete.includes(key) || (val.path && pathsToDelete.includes(val.path))) {
|
|
452
|
+
toRemoveKeys.push(key);
|
|
453
|
+
}
|
|
89
454
|
}
|
|
90
|
-
|
|
91
|
-
|
|
455
|
+
for (const key of toRemoveKeys) {
|
|
456
|
+
this.managedArtifacts.delete(key);
|
|
457
|
+
removed.push(key);
|
|
92
458
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
commands.push(`AddBlendSpaceSample ${assetName} ${sample.animation} ${coords}`);
|
|
459
|
+
for (const path of pathsToDelete) {
|
|
460
|
+
if (!removed.includes(path)) {
|
|
461
|
+
removed.push(path);
|
|
97
462
|
}
|
|
98
463
|
}
|
|
99
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
100
464
|
return {
|
|
101
465
|
success: true,
|
|
102
|
-
message: `
|
|
103
|
-
|
|
466
|
+
message: `Cleanup attempt processed for ${pathsToDelete.length} artifacts${bridgeMessage}`,
|
|
467
|
+
removed
|
|
104
468
|
};
|
|
105
469
|
}
|
|
106
470
|
catch (err) {
|
|
107
|
-
|
|
471
|
+
const error = String(err);
|
|
472
|
+
return {
|
|
473
|
+
success: false,
|
|
474
|
+
message: `Failed to cleanup animation artifacts: ${error}`,
|
|
475
|
+
error
|
|
476
|
+
};
|
|
108
477
|
}
|
|
109
478
|
}
|
|
110
|
-
async
|
|
479
|
+
async createAnimationAsset(params) {
|
|
111
480
|
try {
|
|
112
|
-
const targetPath = params.savePath ?? '/Game/Animations';
|
|
481
|
+
const targetPath = (params.path || params.savePath) ?? '/Game/Animations';
|
|
113
482
|
const validation = validateAssetParams({ name: params.name, savePath: targetPath });
|
|
114
483
|
if (!validation.valid) {
|
|
115
|
-
|
|
484
|
+
const message = validation.error ?? 'Invalid asset parameters';
|
|
485
|
+
return { success: false, message, error: message };
|
|
116
486
|
}
|
|
117
487
|
const sanitized = validation.sanitized;
|
|
118
488
|
const assetName = sanitized.name;
|
|
119
489
|
const assetPath = sanitized.savePath ?? targetPath;
|
|
120
490
|
const fullPath = `${assetPath}/${assetName}`;
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
491
|
+
const normalizedType = (params.assetType || 'sequence').toLowerCase();
|
|
492
|
+
if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
|
|
493
|
+
try {
|
|
494
|
+
const payload = {
|
|
495
|
+
action: 'create_animation_asset',
|
|
496
|
+
name: assetName,
|
|
497
|
+
savePath: assetPath,
|
|
498
|
+
skeletonPath: params.skeletonPath,
|
|
499
|
+
assetType: normalizedType
|
|
500
|
+
};
|
|
501
|
+
const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload), { timeoutMs: 60000 });
|
|
502
|
+
const result = resp?.result ?? resp;
|
|
503
|
+
const resultObj = result && typeof result === 'object' ? result : undefined;
|
|
504
|
+
const isSuccess = resp && resp.success !== false && !!resultObj;
|
|
505
|
+
if (isSuccess && resultObj) {
|
|
506
|
+
const assetPathResult = typeof resultObj.assetPath === 'string' ? resultObj.assetPath : fullPath;
|
|
507
|
+
const assetTypeResult = typeof resultObj.assetType === 'string' ? resultObj.assetType : undefined;
|
|
508
|
+
const existingAsset = typeof resultObj.existingAsset === 'boolean' ? Boolean(resultObj.existingAsset) : false;
|
|
509
|
+
this.trackArtifact(assetName, { path: assetPathResult, type: 'AnimationAsset' });
|
|
510
|
+
return {
|
|
511
|
+
success: true,
|
|
512
|
+
message: resp.message || `Animation asset created at ${assetPathResult}`,
|
|
513
|
+
path: assetPathResult,
|
|
514
|
+
assetType: assetTypeResult,
|
|
515
|
+
existingAsset
|
|
516
|
+
};
|
|
130
517
|
}
|
|
518
|
+
const message = typeof resp?.message === 'string'
|
|
519
|
+
? resp.message
|
|
520
|
+
: (typeof resp?.error === 'string' ? resp.error : 'Animation asset creation failed');
|
|
521
|
+
const error = typeof resp?.error === 'string' ? resp.error : message;
|
|
522
|
+
return { success: false, message, error };
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
const error = String(err);
|
|
526
|
+
return {
|
|
527
|
+
success: false,
|
|
528
|
+
message: `Failed to create animation asset: ${error}`,
|
|
529
|
+
error
|
|
530
|
+
};
|
|
131
531
|
}
|
|
132
532
|
}
|
|
133
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
134
533
|
return {
|
|
135
|
-
success:
|
|
136
|
-
message:
|
|
137
|
-
|
|
534
|
+
success: false,
|
|
535
|
+
message: 'Automation bridge not connected for createAnimationAsset',
|
|
536
|
+
error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
|
|
138
537
|
};
|
|
139
538
|
}
|
|
140
539
|
catch (err) {
|
|
141
|
-
|
|
540
|
+
const error = String(err);
|
|
541
|
+
return {
|
|
542
|
+
success: false,
|
|
543
|
+
message: `Failed to create animation asset: ${error}`,
|
|
544
|
+
error
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async addNotify(params) {
|
|
549
|
+
try {
|
|
550
|
+
const rawPath = (params.animationPath || params.assetPath || '').trim();
|
|
551
|
+
if (!rawPath) {
|
|
552
|
+
const error = 'animationPath or assetPath is required for addNotify';
|
|
553
|
+
return { success: false, message: error, error };
|
|
554
|
+
}
|
|
555
|
+
const notifyName = (params.notifyName || 'Notify').trim();
|
|
556
|
+
const time = typeof params.time === 'number' && params.time >= 0 ? params.time : 0;
|
|
557
|
+
if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
|
|
558
|
+
try {
|
|
559
|
+
const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
|
|
560
|
+
action: 'add_notify',
|
|
561
|
+
assetPath: rawPath,
|
|
562
|
+
notifyName,
|
|
563
|
+
time
|
|
564
|
+
}), { timeoutMs: 60000 });
|
|
565
|
+
const result = resp?.result ?? resp;
|
|
566
|
+
const resultObj = result && typeof result === 'object' ? result : undefined;
|
|
567
|
+
if (resp && resp.success !== false && resultObj) {
|
|
568
|
+
return {
|
|
569
|
+
success: true,
|
|
570
|
+
message: resp.message || `Notify '${notifyName}' added to ${rawPath} at time ${time}`,
|
|
571
|
+
assetPath: typeof resultObj.assetPath === 'string' ? resultObj.assetPath : rawPath,
|
|
572
|
+
notifyName,
|
|
573
|
+
time
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
catch (err) {
|
|
578
|
+
const error = String(err);
|
|
579
|
+
return {
|
|
580
|
+
success: false,
|
|
581
|
+
message: `Failed to add notify: ${error}`,
|
|
582
|
+
error
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
success: false,
|
|
588
|
+
message: 'Automation bridge not connected for addNotify',
|
|
589
|
+
error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
const error = String(err);
|
|
594
|
+
return {
|
|
595
|
+
success: false,
|
|
596
|
+
message: `Failed to add notify: ${error}`,
|
|
597
|
+
error
|
|
598
|
+
};
|
|
142
599
|
}
|
|
143
600
|
}
|
|
144
601
|
async createLevelSequence(params) {
|
|
@@ -179,380 +636,22 @@ export class AnimationTools {
|
|
|
179
636
|
}
|
|
180
637
|
async playAnimation(params) {
|
|
181
638
|
try {
|
|
182
|
-
const
|
|
639
|
+
const commands = [
|
|
640
|
+
`PlayAnimation ${params.actorName} ${params.animationType} ${params.animationPath} ${params.playRate ?? 1.0} ${params.loop ?? false} ${params.blendInTime ?? 0.25} ${params.blendOutTime ?? 0.25}`
|
|
641
|
+
];
|
|
642
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
643
|
+
return {
|
|
644
|
+
success: true,
|
|
645
|
+
message: `Animation ${params.animationType} triggered on ${params.actorName}`,
|
|
183
646
|
actorName: params.actorName,
|
|
184
647
|
animationType: params.animationType,
|
|
185
|
-
|
|
186
|
-
playRate: params.playRate ?? 1.0,
|
|
187
|
-
loop: params.loop ?? false,
|
|
188
|
-
blendInTime: params.blendInTime ?? 0.25,
|
|
189
|
-
blendOutTime: params.blendOutTime ?? 0.25
|
|
190
|
-
});
|
|
191
|
-
const response = await this.bridge.executePython(script);
|
|
192
|
-
const interpreted = interpretStandardResult(response, {
|
|
193
|
-
successMessage: `Animation ${params.animationType} triggered on ${params.actorName}`,
|
|
194
|
-
failureMessage: `Failed to play animation on ${params.actorName}`
|
|
195
|
-
});
|
|
196
|
-
const payload = interpreted.payload ?? {};
|
|
197
|
-
const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
|
|
198
|
-
const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
|
|
199
|
-
const availableActors = coerceStringArray(payload.availableActors);
|
|
200
|
-
const actorName = coerceString(payload.actorName) ?? params.actorName;
|
|
201
|
-
const animationType = coerceString(payload.animationType) ?? params.animationType;
|
|
202
|
-
const assetPath = coerceString(payload.assetPath) ?? params.animationPath;
|
|
203
|
-
const errorMessage = coerceString(payload.error) ?? interpreted.error ?? `Animation playback failed for ${params.actorName}`;
|
|
204
|
-
if (interpreted.success) {
|
|
205
|
-
const result = {
|
|
206
|
-
success: true,
|
|
207
|
-
message: interpreted.message
|
|
208
|
-
};
|
|
209
|
-
if (warnings && warnings.length > 0) {
|
|
210
|
-
result.warnings = warnings;
|
|
211
|
-
}
|
|
212
|
-
if (details && details.length > 0) {
|
|
213
|
-
result.details = details;
|
|
214
|
-
}
|
|
215
|
-
if (actorName) {
|
|
216
|
-
result.actorName = actorName;
|
|
217
|
-
}
|
|
218
|
-
if (animationType) {
|
|
219
|
-
result.animationType = animationType;
|
|
220
|
-
}
|
|
221
|
-
if (assetPath) {
|
|
222
|
-
result.assetPath = assetPath;
|
|
223
|
-
}
|
|
224
|
-
return result;
|
|
225
|
-
}
|
|
226
|
-
const failure = {
|
|
227
|
-
success: false,
|
|
228
|
-
message: `Failed to play animation: ${errorMessage}`,
|
|
229
|
-
error: errorMessage
|
|
648
|
+
assetPath: params.animationPath
|
|
230
649
|
};
|
|
231
|
-
if (warnings && warnings.length > 0) {
|
|
232
|
-
failure.warnings = warnings;
|
|
233
|
-
}
|
|
234
|
-
if (details && details.length > 0) {
|
|
235
|
-
failure.details = details;
|
|
236
|
-
}
|
|
237
|
-
if (availableActors && availableActors.length > 0) {
|
|
238
|
-
failure.availableActors = availableActors;
|
|
239
|
-
}
|
|
240
|
-
if (actorName) {
|
|
241
|
-
failure.actorName = actorName;
|
|
242
|
-
}
|
|
243
|
-
if (animationType) {
|
|
244
|
-
failure.animationType = animationType;
|
|
245
|
-
}
|
|
246
|
-
if (assetPath) {
|
|
247
|
-
failure.assetPath = assetPath;
|
|
248
|
-
}
|
|
249
|
-
return failure;
|
|
250
650
|
}
|
|
251
651
|
catch (err) {
|
|
252
652
|
const error = `Failed to play animation: ${err}`;
|
|
253
653
|
return { success: false, message: error, error: String(err) };
|
|
254
654
|
}
|
|
255
655
|
}
|
|
256
|
-
buildCreateAnimationBlueprintScript(args) {
|
|
257
|
-
const payload = JSON.stringify(args);
|
|
258
|
-
return `
|
|
259
|
-
import unreal
|
|
260
|
-
import json
|
|
261
|
-
import traceback
|
|
262
|
-
|
|
263
|
-
params = json.loads(${JSON.stringify(payload)})
|
|
264
|
-
|
|
265
|
-
result = {
|
|
266
|
-
"success": False,
|
|
267
|
-
"message": "",
|
|
268
|
-
"error": "",
|
|
269
|
-
"warnings": [],
|
|
270
|
-
"details": [],
|
|
271
|
-
"exists": False,
|
|
272
|
-
"skeleton": params.get("skeletonPath") or ""
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
try:
|
|
276
|
-
asset_path = (params.get("path") or "/Game").rstrip('/')
|
|
277
|
-
asset_name = params.get("name") or ""
|
|
278
|
-
full_path = f"{asset_path}/{asset_name}"
|
|
279
|
-
result["path"] = full_path
|
|
280
|
-
|
|
281
|
-
editor_lib = unreal.EditorAssetLibrary
|
|
282
|
-
asset_subsystem = None
|
|
283
|
-
try:
|
|
284
|
-
asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
|
|
285
|
-
except Exception:
|
|
286
|
-
asset_subsystem = None
|
|
287
|
-
|
|
288
|
-
skeleton_path = params.get("skeletonPath")
|
|
289
|
-
skeleton_asset = None
|
|
290
|
-
if skeleton_path:
|
|
291
|
-
if editor_lib.does_asset_exist(skeleton_path):
|
|
292
|
-
skeleton_asset = editor_lib.load_asset(skeleton_path)
|
|
293
|
-
if skeleton_asset and isinstance(skeleton_asset, unreal.Skeleton):
|
|
294
|
-
result["details"].append(f"Using skeleton: {skeleton_path}")
|
|
295
|
-
result["skeleton"] = skeleton_path
|
|
296
|
-
else:
|
|
297
|
-
result["error"] = f"Skeleton asset invalid at {skeleton_path}"
|
|
298
|
-
result["warnings"].append(result["error"])
|
|
299
|
-
skeleton_asset = None
|
|
300
|
-
else:
|
|
301
|
-
result["error"] = f"Skeleton not found at {skeleton_path}"
|
|
302
|
-
result["warnings"].append(result["error"])
|
|
303
|
-
|
|
304
|
-
if not skeleton_asset:
|
|
305
|
-
raise RuntimeError(result["error"] or f"Skeleton {skeleton_path} unavailable")
|
|
306
|
-
|
|
307
|
-
does_exist = False
|
|
308
|
-
try:
|
|
309
|
-
if asset_subsystem and hasattr(asset_subsystem, 'does_asset_exist'):
|
|
310
|
-
does_exist = asset_subsystem.does_asset_exist(full_path)
|
|
311
|
-
else:
|
|
312
|
-
does_exist = editor_lib.does_asset_exist(full_path)
|
|
313
|
-
except Exception:
|
|
314
|
-
does_exist = editor_lib.does_asset_exist(full_path)
|
|
315
|
-
|
|
316
|
-
if does_exist:
|
|
317
|
-
result["exists"] = True
|
|
318
|
-
loaded = editor_lib.load_asset(full_path)
|
|
319
|
-
if loaded:
|
|
320
|
-
result["success"] = True
|
|
321
|
-
result["message"] = f"Animation Blueprint already exists at {full_path}"
|
|
322
|
-
result["details"].append(result["message"])
|
|
323
|
-
else:
|
|
324
|
-
result["error"] = f"Asset exists but could not be loaded: {full_path}"
|
|
325
|
-
result["warnings"].append(result["error"])
|
|
326
|
-
else:
|
|
327
|
-
factory = unreal.AnimBlueprintFactory()
|
|
328
|
-
if skeleton_asset:
|
|
329
|
-
try:
|
|
330
|
-
factory.target_skeleton = skeleton_asset
|
|
331
|
-
except Exception as assign_error:
|
|
332
|
-
result["warnings"].append(f"Unable to assign skeleton {skeleton_path}: {assign_error}")
|
|
333
|
-
|
|
334
|
-
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
|
|
335
|
-
created = asset_tools.create_asset(
|
|
336
|
-
asset_name=asset_name,
|
|
337
|
-
package_path=asset_path,
|
|
338
|
-
asset_class=unreal.AnimBlueprint,
|
|
339
|
-
factory=factory
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
if created:
|
|
343
|
-
editor_lib.save_asset(full_path, only_if_is_dirty=False)
|
|
344
|
-
result["success"] = True
|
|
345
|
-
result["message"] = f"Animation Blueprint created at {full_path}"
|
|
346
|
-
result["details"].append(result["message"])
|
|
347
|
-
else:
|
|
348
|
-
result["error"] = f"Failed to create Animation Blueprint {asset_name}"
|
|
349
|
-
|
|
350
|
-
except Exception as exc:
|
|
351
|
-
result["error"] = str(exc)
|
|
352
|
-
result["warnings"].append(result["error"])
|
|
353
|
-
tb = traceback.format_exc()
|
|
354
|
-
if tb:
|
|
355
|
-
result.setdefault("details", []).append(tb)
|
|
356
|
-
|
|
357
|
-
if result["success"] and not result.get("message"):
|
|
358
|
-
result["message"] = f"Animation Blueprint created at {result.get('path')}"
|
|
359
|
-
|
|
360
|
-
if not result["success"] and not result.get("error"):
|
|
361
|
-
result["error"] = "Animation Blueprint creation failed"
|
|
362
|
-
|
|
363
|
-
if not result.get("warnings"):
|
|
364
|
-
result.pop("warnings", None)
|
|
365
|
-
if not result.get("details"):
|
|
366
|
-
result.pop("details", None)
|
|
367
|
-
if not result.get("error"):
|
|
368
|
-
result.pop("error", None)
|
|
369
|
-
|
|
370
|
-
print('RESULT:' + json.dumps(result))
|
|
371
|
-
`.trim();
|
|
372
|
-
}
|
|
373
|
-
parseAnimationBlueprintResponse(response, assetName, assetPath) {
|
|
374
|
-
const interpreted = interpretStandardResult(response, {
|
|
375
|
-
successMessage: `Animation Blueprint ${assetName} created`,
|
|
376
|
-
failureMessage: `Failed to create Animation Blueprint ${assetName}`
|
|
377
|
-
});
|
|
378
|
-
const payload = interpreted.payload ?? {};
|
|
379
|
-
const path = coerceString(payload.path) ?? `${assetPath}/${assetName}`;
|
|
380
|
-
const exists = coerceBoolean(payload.exists);
|
|
381
|
-
const skeleton = coerceString(payload.skeleton);
|
|
382
|
-
const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
|
|
383
|
-
const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
|
|
384
|
-
if (interpreted.success) {
|
|
385
|
-
const result = {
|
|
386
|
-
success: true,
|
|
387
|
-
message: interpreted.message,
|
|
388
|
-
path
|
|
389
|
-
};
|
|
390
|
-
if (typeof exists === 'boolean') {
|
|
391
|
-
result.exists = exists;
|
|
392
|
-
}
|
|
393
|
-
if (skeleton) {
|
|
394
|
-
result.skeleton = skeleton;
|
|
395
|
-
}
|
|
396
|
-
if (warnings && warnings.length > 0) {
|
|
397
|
-
result.warnings = warnings;
|
|
398
|
-
}
|
|
399
|
-
if (details && details.length > 0) {
|
|
400
|
-
result.details = details;
|
|
401
|
-
}
|
|
402
|
-
return result;
|
|
403
|
-
}
|
|
404
|
-
const errorMessage = coerceString(payload.error) ?? interpreted.error ?? interpreted.message;
|
|
405
|
-
const failure = {
|
|
406
|
-
success: false,
|
|
407
|
-
message: `Failed to create Animation Blueprint: ${errorMessage}`,
|
|
408
|
-
error: errorMessage,
|
|
409
|
-
path
|
|
410
|
-
};
|
|
411
|
-
if (typeof exists === 'boolean') {
|
|
412
|
-
failure.exists = exists;
|
|
413
|
-
}
|
|
414
|
-
if (skeleton) {
|
|
415
|
-
failure.skeleton = skeleton;
|
|
416
|
-
}
|
|
417
|
-
if (warnings && warnings.length > 0) {
|
|
418
|
-
failure.warnings = warnings;
|
|
419
|
-
}
|
|
420
|
-
if (details && details.length > 0) {
|
|
421
|
-
failure.details = details;
|
|
422
|
-
}
|
|
423
|
-
return failure;
|
|
424
|
-
}
|
|
425
|
-
buildPlayAnimationScript(args) {
|
|
426
|
-
const payload = JSON.stringify(args);
|
|
427
|
-
return `
|
|
428
|
-
import unreal
|
|
429
|
-
import json
|
|
430
|
-
import traceback
|
|
431
|
-
|
|
432
|
-
params = json.loads(${JSON.stringify(payload)})
|
|
433
|
-
|
|
434
|
-
result = {
|
|
435
|
-
"success": False,
|
|
436
|
-
"message": "",
|
|
437
|
-
"error": "",
|
|
438
|
-
"warnings": [],
|
|
439
|
-
"details": [],
|
|
440
|
-
"actorName": params.get("actorName"),
|
|
441
|
-
"animationType": params.get("animationType"),
|
|
442
|
-
"assetPath": params.get("animationPath"),
|
|
443
|
-
"availableActors": []
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
try:
|
|
447
|
-
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
448
|
-
actors = actor_subsystem.get_all_level_actors() if actor_subsystem else []
|
|
449
|
-
target = None
|
|
450
|
-
search = params.get("actorName") or ""
|
|
451
|
-
search_lower = search.lower()
|
|
452
|
-
|
|
453
|
-
for actor in actors:
|
|
454
|
-
if not actor:
|
|
455
|
-
continue
|
|
456
|
-
name = (actor.get_name() or "").lower()
|
|
457
|
-
label = (actor.get_actor_label() or "").lower()
|
|
458
|
-
if search_lower and (search_lower == name or search_lower == label or search_lower in label):
|
|
459
|
-
target = actor
|
|
460
|
-
break
|
|
461
|
-
|
|
462
|
-
if not target:
|
|
463
|
-
result["error"] = f"Actor not found: {search}"
|
|
464
|
-
result["warnings"].append("Actor search yielded no results")
|
|
465
|
-
suggestions = []
|
|
466
|
-
for actor in actors[:20]:
|
|
467
|
-
try:
|
|
468
|
-
suggestions.append(actor.get_actor_label())
|
|
469
|
-
except Exception:
|
|
470
|
-
continue
|
|
471
|
-
if suggestions:
|
|
472
|
-
result["availableActors"] = suggestions
|
|
473
|
-
else:
|
|
474
|
-
try:
|
|
475
|
-
display_name = target.get_actor_label() or target.get_name()
|
|
476
|
-
if display_name:
|
|
477
|
-
result["actorName"] = display_name
|
|
478
|
-
except Exception:
|
|
479
|
-
pass
|
|
480
|
-
|
|
481
|
-
skeletal_component = target.get_component_by_class(unreal.SkeletalMeshComponent)
|
|
482
|
-
if not skeletal_component:
|
|
483
|
-
try:
|
|
484
|
-
skeletal_component = target.get_editor_property('mesh')
|
|
485
|
-
except Exception:
|
|
486
|
-
skeletal_component = None
|
|
487
|
-
|
|
488
|
-
if not skeletal_component:
|
|
489
|
-
result["error"] = "No SkeletalMeshComponent found on actor"
|
|
490
|
-
result["warnings"].append("Actor lacks SkeletalMeshComponent")
|
|
491
|
-
else:
|
|
492
|
-
asset_path = params.get("animationPath")
|
|
493
|
-
if not asset_path or not unreal.EditorAssetLibrary.does_asset_exist(asset_path):
|
|
494
|
-
result["error"] = f"Animation asset not found: {asset_path}"
|
|
495
|
-
result["warnings"].append("Animation asset missing")
|
|
496
|
-
else:
|
|
497
|
-
asset = unreal.EditorAssetLibrary.load_asset(asset_path)
|
|
498
|
-
anim_type = params.get("animationType") or ""
|
|
499
|
-
if anim_type == 'Montage':
|
|
500
|
-
anim_instance = skeletal_component.get_anim_instance()
|
|
501
|
-
if anim_instance:
|
|
502
|
-
try:
|
|
503
|
-
anim_instance.montage_play(asset, params.get("playRate", 1.0))
|
|
504
|
-
result["success"] = True
|
|
505
|
-
result["message"] = f"Montage playing on {result.get('actorName') or search}"
|
|
506
|
-
result["details"].append(result["message"])
|
|
507
|
-
except Exception as play_error:
|
|
508
|
-
result["error"] = f"Failed to play montage: {play_error}"
|
|
509
|
-
result["warnings"].append(result["error"])
|
|
510
|
-
else:
|
|
511
|
-
result["error"] = "AnimInstance not found on SkeletalMeshComponent"
|
|
512
|
-
result["warnings"].append(result["error"])
|
|
513
|
-
elif anim_type == 'Sequence':
|
|
514
|
-
try:
|
|
515
|
-
skeletal_component.play_animation(asset, bool(params.get("loop")))
|
|
516
|
-
try:
|
|
517
|
-
anim_instance = skeletal_component.get_anim_instance()
|
|
518
|
-
if anim_instance:
|
|
519
|
-
anim_instance.set_play_rate(params.get("playRate", 1.0))
|
|
520
|
-
except Exception:
|
|
521
|
-
pass
|
|
522
|
-
result["success"] = True
|
|
523
|
-
result["message"] = f"Sequence playing on {result.get('actorName') or search}"
|
|
524
|
-
result["details"].append(result["message"])
|
|
525
|
-
except Exception as play_error:
|
|
526
|
-
result["error"] = f"Failed to play sequence: {play_error}"
|
|
527
|
-
result["warnings"].append(result["error"])
|
|
528
|
-
else:
|
|
529
|
-
result["error"] = "BlendSpace playback requires Animation Blueprint support"
|
|
530
|
-
result["warnings"].append("Unsupported animation type for direct play")
|
|
531
|
-
|
|
532
|
-
except Exception as exc:
|
|
533
|
-
result["error"] = str(exc)
|
|
534
|
-
result["warnings"].append(result["error"])
|
|
535
|
-
tb = traceback.format_exc()
|
|
536
|
-
if tb:
|
|
537
|
-
result["details"].append(tb)
|
|
538
|
-
|
|
539
|
-
if result["success"] and not result.get("message"):
|
|
540
|
-
result["message"] = f"Animation {result.get('animationType')} triggered on {result.get('actorName') or params.get('actorName')}"
|
|
541
|
-
|
|
542
|
-
if not result["success"] and not result.get("error"):
|
|
543
|
-
result["error"] = "Animation playback failed"
|
|
544
|
-
|
|
545
|
-
if not result.get("warnings"):
|
|
546
|
-
result.pop("warnings", None)
|
|
547
|
-
if not result.get("details"):
|
|
548
|
-
result.pop("details", None)
|
|
549
|
-
if not result.get("availableActors"):
|
|
550
|
-
result.pop("availableActors", None)
|
|
551
|
-
if not result.get("error"):
|
|
552
|
-
result.pop("error", None)
|
|
553
|
-
|
|
554
|
-
print('RESULT:' + json.dumps(result))
|
|
555
|
-
`.trim();
|
|
556
|
-
}
|
|
557
656
|
}
|
|
558
657
|
//# sourceMappingURL=animation.js.map
|