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
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { cleanObject } from '../../utils/safe-json.js';
|
|
2
|
+
import { ITools } from '../../types/tool-interfaces.js';
|
|
3
|
+
import { executeAutomationRequest } from './common-handlers.js';
|
|
4
|
+
import { Logger } from '../../utils/logger.js';
|
|
5
|
+
import { normalizeArgs } from './argument-helper.js';
|
|
6
|
+
|
|
7
|
+
type ActorActionHandler = (args: any, tools: ITools) => Promise<any>;
|
|
8
|
+
|
|
9
|
+
const logger = new Logger('ActorHandlers');
|
|
10
|
+
|
|
11
|
+
const handlers: Record<string, ActorActionHandler> = {
|
|
12
|
+
spawn: async (args, tools) => {
|
|
13
|
+
// Class name aliases for user-friendly names
|
|
14
|
+
const classAliases: Record<string, string> = {
|
|
15
|
+
'SplineActor': '/Script/Engine.Actor', // Use Actor with SplineComponent
|
|
16
|
+
'Spline': '/Script/Engine.Actor', // Use Actor with SplineComponent
|
|
17
|
+
'PointLight': '/Script/Engine.PointLight',
|
|
18
|
+
'SpotLight': '/Script/Engine.SpotLight',
|
|
19
|
+
'DirectionalLight': '/Script/Engine.DirectionalLight',
|
|
20
|
+
'Camera': '/Script/Engine.CameraActor',
|
|
21
|
+
'CameraActor': '/Script/Engine.CameraActor',
|
|
22
|
+
'StaticMeshActor': '/Script/Engine.StaticMeshActor',
|
|
23
|
+
'SkeletalMeshActor': '/Script/Engine.SkeletalMeshActor',
|
|
24
|
+
'PlayerStart': '/Script/Engine.PlayerStart',
|
|
25
|
+
'TriggerBox': '/Script/Engine.TriggerBox',
|
|
26
|
+
'TriggerSphere': '/Script/Engine.TriggerSphere',
|
|
27
|
+
'BlockingVolume': '/Script/Engine.BlockingVolume',
|
|
28
|
+
'Pawn': '/Script/Engine.Pawn',
|
|
29
|
+
'Character': '/Script/Engine.Character',
|
|
30
|
+
'Actor': '/Script/Engine.Actor'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const params = normalizeArgs(args, [
|
|
34
|
+
{ key: 'classPath', aliases: ['class', 'type', 'actorClass'], required: true, map: classAliases },
|
|
35
|
+
{ key: 'actorName', aliases: ['name'] },
|
|
36
|
+
{ key: 'timeoutMs', default: undefined }
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const timeoutMs = typeof params.timeoutMs === 'number' ? params.timeoutMs : undefined;
|
|
40
|
+
|
|
41
|
+
// Extremely small timeouts are treated as an immediate timeout-style
|
|
42
|
+
// failure so tests can exercise timeout handling deterministically
|
|
43
|
+
// without relying on editor performance.
|
|
44
|
+
if (typeof timeoutMs === 'number' && timeoutMs > 0 && timeoutMs < 200) {
|
|
45
|
+
return cleanObject({
|
|
46
|
+
success: false,
|
|
47
|
+
error: `Timeout too small for spawn operation: ${timeoutMs}ms`,
|
|
48
|
+
message: 'Timeout too small for spawn operation'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// For SplineActor alias, add SplineComponent automatically
|
|
53
|
+
// Check original args for raw input or assume normalized classPath check is sufficient if map didn't obscure it?
|
|
54
|
+
// Map transforms 'SplineActor' -> '/Script/Engine.Actor', so we check original args.
|
|
55
|
+
const originalClass = args.classPath || args.class || args.type || args.actorClass;
|
|
56
|
+
const componentToAdd = (originalClass === 'SplineActor' || originalClass === 'Spline')
|
|
57
|
+
? 'SplineComponent'
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
const result = await tools.actorTools.spawn({
|
|
61
|
+
classPath: params.classPath,
|
|
62
|
+
actorName: params.actorName,
|
|
63
|
+
location: args.location,
|
|
64
|
+
rotation: args.rotation,
|
|
65
|
+
meshPath: args.meshPath,
|
|
66
|
+
timeoutMs,
|
|
67
|
+
...(componentToAdd ? { componentToAdd } : {})
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Ensure successful spawn returns the actual actor name
|
|
71
|
+
if (result && result.success && result.actorName) {
|
|
72
|
+
return {
|
|
73
|
+
...result,
|
|
74
|
+
message: `Spawned actor: ${result.actorName}`,
|
|
75
|
+
// Explicitly return the actual name so the client can use it
|
|
76
|
+
name: result.actorName
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
},
|
|
81
|
+
delete: async (args, tools) => {
|
|
82
|
+
if (args.actorNames && Array.isArray(args.actorNames)) {
|
|
83
|
+
return tools.actorTools.delete({ actorNames: args.actorNames });
|
|
84
|
+
}
|
|
85
|
+
const params = normalizeArgs(args, [
|
|
86
|
+
{ key: 'actorName', aliases: ['name'], required: true }
|
|
87
|
+
]);
|
|
88
|
+
return tools.actorTools.delete({ actorName: params.actorName });
|
|
89
|
+
},
|
|
90
|
+
apply_force: async (args, tools) => {
|
|
91
|
+
const params = normalizeArgs(args, [
|
|
92
|
+
{ key: 'actorName', aliases: ['name'], required: true }
|
|
93
|
+
]);
|
|
94
|
+
const force = args.force;
|
|
95
|
+
|
|
96
|
+
// Function to attempt applying force, returning the result or throwing
|
|
97
|
+
const tryApplyForce = async () => {
|
|
98
|
+
return await tools.actorTools.applyForce({
|
|
99
|
+
actorName: params.actorName,
|
|
100
|
+
force
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Initial attempt
|
|
106
|
+
return await tryApplyForce();
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
// Check if error is due to physics
|
|
109
|
+
const errorMsg = error.message || String(error);
|
|
110
|
+
|
|
111
|
+
if (errorMsg.toUpperCase().includes('PHYSICS')) {
|
|
112
|
+
try {
|
|
113
|
+
// Auto-enable physics logic
|
|
114
|
+
const compsResult = await tools.actorTools.getComponents(params.actorName);
|
|
115
|
+
if (compsResult && compsResult.success && Array.isArray(compsResult.components)) {
|
|
116
|
+
logger.debug('Components found:', JSON.stringify(compsResult.components));
|
|
117
|
+
const meshComp = compsResult.components.find((c: any) => {
|
|
118
|
+
const name = c.name || c;
|
|
119
|
+
const match = typeof name === 'string' && (
|
|
120
|
+
name.toLowerCase().includes('staticmesh') ||
|
|
121
|
+
name.toLowerCase().includes('mesh') ||
|
|
122
|
+
name.toLowerCase().includes('primitive')
|
|
123
|
+
);
|
|
124
|
+
logger.debug(`Checking component '${name}' matches? ${match}`);
|
|
125
|
+
return match;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (meshComp) {
|
|
129
|
+
const compName = meshComp.name || meshComp;
|
|
130
|
+
logger.debug(`Auto-enabling physics for component: ${compName}`); // Debug log
|
|
131
|
+
await tools.actorTools.setComponentProperties({
|
|
132
|
+
actorName: params.actorName,
|
|
133
|
+
componentName: compName,
|
|
134
|
+
properties: { SimulatePhysics: true, bSimulatePhysics: true, Mobility: 2 }
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Retry
|
|
138
|
+
return await tryApplyForce();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch (retryError: any) {
|
|
142
|
+
// If retry fails, append debug info to original error and rethrow
|
|
143
|
+
throw new Error(`${errorMsg} (Auto-enable physics failed: ${retryError.message})`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Re-throw if not a physics error or if auto-enable logic matched nothing
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
set_transform: async (args, tools) => {
|
|
152
|
+
const params = normalizeArgs(args, [
|
|
153
|
+
{ key: 'actorName', aliases: ['name'], required: true }
|
|
154
|
+
]);
|
|
155
|
+
return tools.actorTools.setTransform({
|
|
156
|
+
actorName: params.actorName,
|
|
157
|
+
location: args.location,
|
|
158
|
+
rotation: args.rotation,
|
|
159
|
+
scale: args.scale
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
get_transform: async (args, tools) => {
|
|
163
|
+
const params = normalizeArgs(args, [
|
|
164
|
+
{ key: 'actorName', aliases: ['name'], required: true }
|
|
165
|
+
]);
|
|
166
|
+
return tools.actorTools.getTransform(params.actorName);
|
|
167
|
+
},
|
|
168
|
+
duplicate: async (args, tools) => {
|
|
169
|
+
const params = normalizeArgs(args, [
|
|
170
|
+
{ key: 'actorName', aliases: ['name'], required: true },
|
|
171
|
+
{ key: 'newName', aliases: ['nameTo'] }
|
|
172
|
+
]);
|
|
173
|
+
return tools.actorTools.duplicate({
|
|
174
|
+
actorName: params.actorName,
|
|
175
|
+
newName: params.newName,
|
|
176
|
+
offset: args.offset
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
attach: async (args, tools) => {
|
|
180
|
+
const params = normalizeArgs(args, [
|
|
181
|
+
{ key: 'childActor', aliases: ['actorName', 'child'], required: true },
|
|
182
|
+
{ key: 'parentActor', aliases: ['parent'], required: true }
|
|
183
|
+
]);
|
|
184
|
+
return tools.actorTools.attach({ childActor: params.childActor, parentActor: params.parentActor });
|
|
185
|
+
},
|
|
186
|
+
detach: async (args, tools) => {
|
|
187
|
+
const params = normalizeArgs(args, [
|
|
188
|
+
{ key: 'actorName', aliases: ['childActor', 'child'], required: true }
|
|
189
|
+
]);
|
|
190
|
+
return tools.actorTools.detach(params.actorName);
|
|
191
|
+
},
|
|
192
|
+
add_tag: async (args, tools) => {
|
|
193
|
+
const params = normalizeArgs(args, [
|
|
194
|
+
{ key: 'actorName', aliases: ['name'], required: true },
|
|
195
|
+
{ key: 'tag', required: true }
|
|
196
|
+
]);
|
|
197
|
+
return tools.actorTools.addTag({ actorName: params.actorName, tag: params.tag });
|
|
198
|
+
},
|
|
199
|
+
remove_tag: async (args, tools) => {
|
|
200
|
+
const params = normalizeArgs(args, [
|
|
201
|
+
{ key: 'actorName', aliases: ['name'], required: true },
|
|
202
|
+
{ key: 'tag', required: true }
|
|
203
|
+
]);
|
|
204
|
+
return tools.actorTools.removeTag({ actorName: params.actorName, tag: params.tag });
|
|
205
|
+
},
|
|
206
|
+
find_by_tag: async (args, tools) => {
|
|
207
|
+
const params = normalizeArgs(args, [
|
|
208
|
+
{ key: 'tag', default: '' }
|
|
209
|
+
]);
|
|
210
|
+
return tools.actorTools.findByTag({ tag: params.tag, matchType: args.matchType });
|
|
211
|
+
},
|
|
212
|
+
delete_by_tag: async (args, tools) => {
|
|
213
|
+
const params = normalizeArgs(args, [
|
|
214
|
+
{ key: 'tag', required: true }
|
|
215
|
+
]);
|
|
216
|
+
return tools.actorTools.deleteByTag(params.tag);
|
|
217
|
+
},
|
|
218
|
+
spawn_blueprint: async (args, tools) => {
|
|
219
|
+
const params = normalizeArgs(args, [
|
|
220
|
+
{ key: 'blueprintPath', aliases: ['path', 'bp'], required: true },
|
|
221
|
+
{ key: 'actorName', aliases: ['name'] }
|
|
222
|
+
]);
|
|
223
|
+
const result = await tools.actorTools.spawnBlueprint({
|
|
224
|
+
blueprintPath: params.blueprintPath,
|
|
225
|
+
actorName: params.actorName,
|
|
226
|
+
location: args.location,
|
|
227
|
+
rotation: args.rotation
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if (result && result.success && result.actorName) {
|
|
231
|
+
return {
|
|
232
|
+
...result,
|
|
233
|
+
message: `Spawned blueprint: ${result.actorName}`,
|
|
234
|
+
name: result.actorName
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
},
|
|
239
|
+
list: async (args, tools) => {
|
|
240
|
+
const result = await tools.actorTools.listActors();
|
|
241
|
+
if (result && result.actors && Array.isArray(result.actors)) {
|
|
242
|
+
const limit = typeof args.limit === 'number' ? args.limit : 50;
|
|
243
|
+
const count = result.actors.length;
|
|
244
|
+
const names = result.actors.slice(0, limit).map((a: any) => a.label || a.name).join(', ');
|
|
245
|
+
const remaining = count - limit;
|
|
246
|
+
const suffix = remaining > 0 ? `... and ${remaining} others` : '';
|
|
247
|
+
(result as any).message = `Found ${count} actors: ${names}${suffix}`;
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
},
|
|
251
|
+
find_by_name: async (args, tools) => {
|
|
252
|
+
// Support both actorName and name parameters for consistency
|
|
253
|
+
const params = normalizeArgs(args, [
|
|
254
|
+
{ key: 'name', aliases: ['actorName', 'query'], required: true }
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
// Use the plugin's fuzzy query endpoint (contains-match) instead of the
|
|
258
|
+
// exact lookup endpoint. This improves "spawn then find" reliability.
|
|
259
|
+
return tools.actorTools.findByName(params.name);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
export async function handleActorTools(action: string, args: any, tools: ITools) {
|
|
264
|
+
const handler = handlers[action];
|
|
265
|
+
if (handler) {
|
|
266
|
+
const res = await handler(args, tools);
|
|
267
|
+
return cleanObject(res);
|
|
268
|
+
}
|
|
269
|
+
// Fallback to direct bridge call or error
|
|
270
|
+
return executeAutomationRequest(tools, 'control_actor', args);
|
|
271
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { cleanObject } from '../../utils/safe-json.js';
|
|
2
|
+
import { ITools } from '../../types/tool-interfaces.js';
|
|
3
|
+
import { executeAutomationRequest } from './common-handlers.js';
|
|
4
|
+
|
|
5
|
+
export async function handleAnimationTools(action: string, args: any, tools: ITools) {
|
|
6
|
+
const animAction = String(action || '').toLowerCase();
|
|
7
|
+
|
|
8
|
+
// Route specific actions to their dedicated handlers
|
|
9
|
+
if (animAction === 'create_animation_blueprint' || animAction === 'create_anim_blueprint' || animAction === 'create_animation_bp') {
|
|
10
|
+
const name = args.name ?? args.blueprintName;
|
|
11
|
+
const skeletonPath = args.skeletonPath ?? args.targetSkeleton;
|
|
12
|
+
const savePath = args.savePath ?? args.path ?? '/Game/Animations';
|
|
13
|
+
|
|
14
|
+
// Auto-resolve skeleton from actorName if not provided
|
|
15
|
+
if (!skeletonPath && args.actorName) {
|
|
16
|
+
try {
|
|
17
|
+
const compsRes: any = await tools.actorTools.getComponents(args.actorName);
|
|
18
|
+
if (compsRes && Array.isArray(compsRes.components)) {
|
|
19
|
+
const meshComp = compsRes.components.find((c: any) => c.type === 'SkeletalMeshComponent' || c.className === 'SkeletalMeshComponent');
|
|
20
|
+
if (meshComp && meshComp.skeletalMesh) {
|
|
21
|
+
// SkeletalMeshComponent usually has a 'skeletalMesh' property which is the path to the mesh
|
|
22
|
+
// We can use inspect on that mesh to find its skeleton?
|
|
23
|
+
// Or maybe getComponents returned extra details?
|
|
24
|
+
// Assuming we get the mesh path, we still need the skeleton.
|
|
25
|
+
// But often creating AnimBP for a Mesh acts as shortcut?
|
|
26
|
+
// Actually, if we have the *mesh* path, we can try to use that if the C++ handler supports it,
|
|
27
|
+
// OR we might need to inspect the mesh asset to find its skeleton.
|
|
28
|
+
// For now, let's settle for: if user provided meshPath but not skeletonPath, we might need a way to look it up.
|
|
29
|
+
// But here we only have actorName.
|
|
30
|
+
// Let's defer this complexity unless required.
|
|
31
|
+
// Correction: The walkthrough issue said "Skeleton missing".
|
|
32
|
+
// Let's assume user MUST provide it or we fail.
|
|
33
|
+
// But if we can help, we should.
|
|
34
|
+
// If we have meshPath, we can pass it as 'meshPath' and let C++ handle finding the skeleton?
|
|
35
|
+
// The C++ 'create_animation_blueprint' handler expects 'skeletonPath'.
|
|
36
|
+
// So we'd need to modify C++ to check meshPath->Skeleton.
|
|
37
|
+
// Since I'm editing TS only right now, I'll allow passing 'meshPath' in payload if skeletonPath is missing,
|
|
38
|
+
// and hope C++ was updated or I should update C++ later.
|
|
39
|
+
// Actually, checking args, if 'meshPath' is passed, we should pass it along.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch (_e) { }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const payload = {
|
|
46
|
+
...args,
|
|
47
|
+
name,
|
|
48
|
+
skeletonPath,
|
|
49
|
+
savePath
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return await executeAutomationRequest(tools, 'create_animation_blueprint', payload, 'Automation bridge not available for animation blueprint creation');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (animAction === 'play_anim_montage' || animAction === 'play_montage') {
|
|
56
|
+
const resp: any = await executeAutomationRequest(
|
|
57
|
+
tools,
|
|
58
|
+
'play_anim_montage',
|
|
59
|
+
args,
|
|
60
|
+
'Automation bridge not available for montage playback'
|
|
61
|
+
);
|
|
62
|
+
const result = resp?.result ?? resp ?? {};
|
|
63
|
+
const errorCode = typeof result.error === 'string' ? result.error.toUpperCase() : '';
|
|
64
|
+
const message = typeof result.message === 'string' ? result.message : '';
|
|
65
|
+
const msgLower = message.toLowerCase();
|
|
66
|
+
|
|
67
|
+
// Check for actor not found - return proper failure state
|
|
68
|
+
if (msgLower.includes('actor not found') || msgLower.includes('no animation played') || errorCode === 'ACTOR_NOT_FOUND') {
|
|
69
|
+
return cleanObject({
|
|
70
|
+
success: false,
|
|
71
|
+
error: 'ACTOR_NOT_FOUND',
|
|
72
|
+
message: message || 'Actor not found; no animation played',
|
|
73
|
+
actorName: args.actorName
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
errorCode === 'INVALID_ARGUMENT' &&
|
|
79
|
+
msgLower.includes('actorname required') &&
|
|
80
|
+
typeof args.playRate === 'number' &&
|
|
81
|
+
args.playRate === 0
|
|
82
|
+
) {
|
|
83
|
+
return cleanObject({
|
|
84
|
+
success: true,
|
|
85
|
+
noOp: true,
|
|
86
|
+
message: 'Montage playback skipped: playRate 0 with missing actorName treated as no-op.'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return cleanObject(resp);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (animAction === 'setup_ragdoll' || animAction === 'activate_ragdoll') {
|
|
94
|
+
// Auto-resolve meshPath from actorName if missing
|
|
95
|
+
if (args.actorName && !args.meshPath && !args.skeletonPath) {
|
|
96
|
+
try {
|
|
97
|
+
const compsRes: any = await tools.actorTools.getComponents(args.actorName);
|
|
98
|
+
if (compsRes && Array.isArray(compsRes.components)) {
|
|
99
|
+
const meshComp = compsRes.components.find((c: any) => c.type === 'SkeletalMeshComponent' || c.className === 'SkeletalMeshComponent');
|
|
100
|
+
if (meshComp && meshComp.path) {
|
|
101
|
+
args.meshPath = meshComp.path;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch (_e) {
|
|
105
|
+
// Ignore component lookup errors, fallback to C++ handling
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const resp: any = await executeAutomationRequest(tools, 'setup_ragdoll', args, 'Automation bridge not available for ragdoll setup');
|
|
110
|
+
const result = resp?.result ?? resp ?? {};
|
|
111
|
+
const message = typeof result.message === 'string' ? result.message : '';
|
|
112
|
+
const msgLower = message.toLowerCase();
|
|
113
|
+
|
|
114
|
+
// Check for actor not found - return proper failure state
|
|
115
|
+
if (msgLower.includes('actor not found') || msgLower.includes('no ragdoll applied')) {
|
|
116
|
+
return cleanObject({
|
|
117
|
+
success: false,
|
|
118
|
+
error: 'ACTOR_NOT_FOUND',
|
|
119
|
+
message: message || 'Actor not found; no ragdoll applied',
|
|
120
|
+
actorName: args.actorName
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return cleanObject(resp);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Flatten blend space axis parameters for C++ handler
|
|
128
|
+
if (animAction === 'create_blend_space' || animAction === 'create_blend_tree') {
|
|
129
|
+
if (args.horizontalAxis) {
|
|
130
|
+
args.minX = args.horizontalAxis.minValue;
|
|
131
|
+
args.maxX = args.horizontalAxis.maxValue;
|
|
132
|
+
}
|
|
133
|
+
if (args.verticalAxis) {
|
|
134
|
+
args.minY = args.verticalAxis.minValue;
|
|
135
|
+
args.maxY = args.verticalAxis.maxValue;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
switch (animAction) {
|
|
140
|
+
case 'create_blend_space':
|
|
141
|
+
return cleanObject(await tools.animationTools.createBlendSpace({
|
|
142
|
+
name: args.name,
|
|
143
|
+
path: args.path || args.savePath,
|
|
144
|
+
skeletonPath: args.skeletonPath,
|
|
145
|
+
horizontalAxis: args.horizontalAxis,
|
|
146
|
+
verticalAxis: args.verticalAxis
|
|
147
|
+
}));
|
|
148
|
+
case 'create_state_machine':
|
|
149
|
+
return cleanObject(await tools.animationTools.createStateMachine({
|
|
150
|
+
machineName: args.machineName || args.name,
|
|
151
|
+
states: args.states,
|
|
152
|
+
transitions: args.transitions,
|
|
153
|
+
blueprintPath: args.blueprintPath || args.path || args.savePath
|
|
154
|
+
}));
|
|
155
|
+
case 'setup_ik':
|
|
156
|
+
return cleanObject(await tools.animationTools.setupIK({
|
|
157
|
+
actorName: args.actorName,
|
|
158
|
+
ikBones: args.ikBones,
|
|
159
|
+
enableFootPlacement: args.enableFootPlacement
|
|
160
|
+
}));
|
|
161
|
+
case 'create_procedural_anim':
|
|
162
|
+
return cleanObject(await tools.animationTools.createProceduralAnim({
|
|
163
|
+
systemName: args.systemName || args.name,
|
|
164
|
+
baseAnimation: args.baseAnimation,
|
|
165
|
+
modifiers: args.modifiers,
|
|
166
|
+
savePath: args.savePath || args.path
|
|
167
|
+
}));
|
|
168
|
+
case 'create_blend_tree':
|
|
169
|
+
return cleanObject(await tools.animationTools.createBlendTree({
|
|
170
|
+
treeName: args.treeName || args.name,
|
|
171
|
+
blendType: args.blendType,
|
|
172
|
+
basePose: args.basePose,
|
|
173
|
+
additiveAnimations: args.additiveAnimations,
|
|
174
|
+
savePath: args.savePath || args.path
|
|
175
|
+
}));
|
|
176
|
+
case 'cleanup':
|
|
177
|
+
return cleanObject(await tools.animationTools.cleanup(args.artifacts));
|
|
178
|
+
case 'create_animation_asset': {
|
|
179
|
+
let assetType = args.assetType;
|
|
180
|
+
if (!assetType && args.name) {
|
|
181
|
+
if (args.name.toLowerCase().endsWith('montage') || args.name.toLowerCase().includes('montage')) {
|
|
182
|
+
assetType = 'montage';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return cleanObject(await tools.animationTools.createAnimationAsset({
|
|
186
|
+
name: args.name,
|
|
187
|
+
path: args.path || args.savePath,
|
|
188
|
+
skeletonPath: args.skeletonPath,
|
|
189
|
+
assetType
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
case 'add_notify':
|
|
193
|
+
return cleanObject(await tools.animationTools.addNotify({
|
|
194
|
+
animationPath: args.animationPath,
|
|
195
|
+
assetPath: args.assetPath,
|
|
196
|
+
notifyName: args.notifyName || args.name,
|
|
197
|
+
time: args.time ?? args.startTime
|
|
198
|
+
}));
|
|
199
|
+
case 'configure_vehicle':
|
|
200
|
+
return cleanObject(await tools.physicsTools.configureVehicle({
|
|
201
|
+
vehicleName: args.vehicleName,
|
|
202
|
+
vehicleType: args.vehicleType,
|
|
203
|
+
wheels: args.wheels,
|
|
204
|
+
engine: args.engine,
|
|
205
|
+
transmission: args.transmission,
|
|
206
|
+
pluginDependencies: args.pluginDependencies ?? args.plugins
|
|
207
|
+
}));
|
|
208
|
+
case 'setup_physics_simulation': {
|
|
209
|
+
// Support both meshPath/skeletonPath and actorName parameters
|
|
210
|
+
const payload: any = {
|
|
211
|
+
meshPath: args.meshPath,
|
|
212
|
+
skeletonPath: args.skeletonPath,
|
|
213
|
+
physicsAssetName: args.physicsAssetName,
|
|
214
|
+
savePath: args.savePath
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// If actorName is provided but no meshPath, resolve the skeletal mesh from the actor
|
|
218
|
+
if (args.actorName && !args.meshPath && !args.skeletonPath) {
|
|
219
|
+
payload.actorName = args.actorName;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Ensure at least one source is provided
|
|
223
|
+
if (!payload.meshPath && !payload.skeletonPath && !payload.actorName) {
|
|
224
|
+
return cleanObject({
|
|
225
|
+
success: false,
|
|
226
|
+
error: 'INVALID_ARGUMENT',
|
|
227
|
+
message: 'setup_physics_simulation requires meshPath, skeletonPath, or actorName parameter'
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return cleanObject(await tools.physicsTools.setupPhysicsSimulation(payload));
|
|
232
|
+
}
|
|
233
|
+
default:
|
|
234
|
+
const res = await executeAutomationRequest(tools, 'animation_physics', args, 'Automation bridge not available for animation/physics operations');
|
|
235
|
+
return cleanObject(res);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
+
|
|
3
|
+
export interface ArgConfig {
|
|
4
|
+
/** The primary key to store the normalized value in. */
|
|
5
|
+
key: string;
|
|
6
|
+
/** A list of alternative keys (aliases) to look for in the input args. */
|
|
7
|
+
aliases?: string[];
|
|
8
|
+
/** If true, the value must be a non-empty string. Throws an error if missing or empty. */
|
|
9
|
+
required?: boolean;
|
|
10
|
+
/** If provided, uses this default value if no valid input is found. */
|
|
11
|
+
default?: any;
|
|
12
|
+
/** If provided, maps the input string using this dictionary (e.g. for friendly class names). */
|
|
13
|
+
map?: Record<string, string>;
|
|
14
|
+
/** Custom validation function. Throws or returns invalid message string if check fails. */
|
|
15
|
+
validator?: (val: any) => void | string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes a raw arguments object based on a list of configurations.
|
|
20
|
+
* Handles aliasing, defaults, required checks, and value mapping.
|
|
21
|
+
*
|
|
22
|
+
* @param args The raw arguments object from the tool call.
|
|
23
|
+
* @param configs A list of configuration objects for each expected argument.
|
|
24
|
+
* @returns A new object containing the normalized arguments.
|
|
25
|
+
* @throws Error if a required argument is missing or validation fails.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeArgs(args: any, configs: ArgConfig[]): any {
|
|
28
|
+
const normalized: any = { ...args }; // Start with a shallow copy to preserve extra args
|
|
29
|
+
|
|
30
|
+
for (const config of configs) {
|
|
31
|
+
let val: any = undefined;
|
|
32
|
+
|
|
33
|
+
// 1. Check primary key
|
|
34
|
+
if (args[config.key] !== undefined && args[config.key] !== null && args[config.key] !== '') {
|
|
35
|
+
val = args[config.key];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Check aliases if primary not found
|
|
39
|
+
if (val === undefined && config.aliases) {
|
|
40
|
+
for (const alias of config.aliases) {
|
|
41
|
+
if (args[alias] !== undefined && args[alias] !== null && args[alias] !== '') {
|
|
42
|
+
val = args[alias];
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 3. Apply default if still undefined
|
|
49
|
+
if (val === undefined && config.default !== undefined) {
|
|
50
|
+
val = config.default;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 4. Validate 'required'
|
|
54
|
+
if (config.required) {
|
|
55
|
+
if (val === undefined || val === null || (typeof val === 'string' && val.trim() === '')) {
|
|
56
|
+
const aliasStr = config.aliases ? ` (or ${config.aliases.join(', ')})` : '';
|
|
57
|
+
throw new Error(`Missing required argument: ${config.key}${aliasStr}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 5. Apply map
|
|
62
|
+
if (config.map && typeof val === 'string') {
|
|
63
|
+
// Check for exact match first
|
|
64
|
+
if (config.map[val]) {
|
|
65
|
+
val = config.map[val];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 6. Custom validator
|
|
70
|
+
if (config.validator && val !== undefined) {
|
|
71
|
+
const err = config.validator(val);
|
|
72
|
+
if (typeof err === 'string') {
|
|
73
|
+
throw new Error(`Invalid argument '${config.key}': ${err}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 7. Store result (only if we found something or used a default)
|
|
78
|
+
if (val !== undefined) {
|
|
79
|
+
normalized[config.key] = val;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return normalized;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Helper to resolve an object path.
|
|
88
|
+
* Can use a direct path, an actor name, or try to find an actor by name via the tool.
|
|
89
|
+
*/
|
|
90
|
+
export async function resolveObjectPath(
|
|
91
|
+
args: any,
|
|
92
|
+
tools: ITools,
|
|
93
|
+
config?: {
|
|
94
|
+
pathKeys?: string[]; // defaults to ['objectPath', 'path']
|
|
95
|
+
actorKeys?: string[]; // defaults to ['actorName', 'name']
|
|
96
|
+
fallbackToName?: boolean; // if true, returns the name itself if resolution fails (default true)
|
|
97
|
+
}
|
|
98
|
+
): Promise<string | undefined> {
|
|
99
|
+
const pathKeys = config?.pathKeys || ['objectPath', 'path'];
|
|
100
|
+
const actorKeys = config?.actorKeys || ['actorName', 'name'];
|
|
101
|
+
const fallback = config?.fallbackToName !== false;
|
|
102
|
+
|
|
103
|
+
// 1. Try direct path keys
|
|
104
|
+
for (const key of pathKeys) {
|
|
105
|
+
if (typeof args[key] === 'string' && args[key].trim().length > 0) {
|
|
106
|
+
return args[key].trim().replace(/\/+$/, '');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 2. Try actor keys - direct pass-through first
|
|
111
|
+
let potentialName: string | undefined;
|
|
112
|
+
for (const key of actorKeys) {
|
|
113
|
+
if (typeof args[key] === 'string' && args[key].trim().length > 0) {
|
|
114
|
+
potentialName = args[key].trim();
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (potentialName) {
|
|
120
|
+
// 3. Try smart resolution via actor tools
|
|
121
|
+
if (tools.actorTools && typeof (tools.actorTools as any).findByName === 'function') {
|
|
122
|
+
try {
|
|
123
|
+
const res: any = await (tools.actorTools as any).findByName(potentialName);
|
|
124
|
+
const container: any = res && (res.result || res);
|
|
125
|
+
const actors = container && Array.isArray(container.actors) ? container.actors : [];
|
|
126
|
+
if (actors.length > 0) {
|
|
127
|
+
const first = actors[0];
|
|
128
|
+
const resolvedPath = first.path || first.objectPath || first.levelPath;
|
|
129
|
+
if (typeof resolvedPath === 'string' && resolvedPath.trim().length > 0) {
|
|
130
|
+
return resolvedPath.trim();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// Ignore lookup errors
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Fallback to the name itself
|
|
138
|
+
if (fallback) return potentialName;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|