unreal-engine-mcp-server 0.4.6 → 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 +269 -22
- package/CONTRIBUTING.md +140 -0
- package/README.md +166 -72
- 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 -604
- 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 +5475 -1627
- package/dist/tools/consolidated-tool-definitions.js +829 -482
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +211 -1009
- 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 +45 -0
- package/dist/tools/logs.js +210 -0
- 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 +195 -11
- 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 -649
- 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 -500
- package/src/tools/consolidated-tool-handlers.ts +272 -1122
- 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 +219 -0
- 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 +250 -13
- 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 -572
- 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/src/tools/foliage.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// Foliage tools for Unreal Engine
|
|
2
2
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
3
|
-
import {
|
|
3
|
+
import { AutomationBridge } from '../automation/index.js';
|
|
4
|
+
import { coerceBoolean, coerceNumber, coerceString } from '../utils/result-helpers.js';
|
|
4
5
|
|
|
5
6
|
export class FoliageTools {
|
|
6
|
-
constructor(private bridge: UnrealBridge) {}
|
|
7
|
+
constructor(private bridge: UnrealBridge, private automationBridge?: AutomationBridge) { }
|
|
8
|
+
|
|
9
|
+
setAutomationBridge(automationBridge?: AutomationBridge) { this.automationBridge = automationBridge; }
|
|
7
10
|
|
|
8
11
|
// NOTE: We intentionally avoid issuing Unreal console commands here because
|
|
9
12
|
// they have proven unreliable and generate engine warnings (failed FindConsoleObject).
|
|
@@ -49,196 +52,60 @@ export class FoliageTools {
|
|
|
49
52
|
return { success: false, error: errors.join('; ') };
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
factory = unreal.FoliageType_InstancedStaticMeshFactory()
|
|
102
|
-
# Try different property names for different UE versions
|
|
103
|
-
try:
|
|
104
|
-
factory.set_editor_property('mesh', mesh)
|
|
105
|
-
except:
|
|
106
|
-
try:
|
|
107
|
-
factory.set_editor_property('static_mesh', mesh)
|
|
108
|
-
except:
|
|
109
|
-
try:
|
|
110
|
-
factory.set_editor_property('source_mesh', mesh)
|
|
111
|
-
except:
|
|
112
|
-
pass # Factory will use default or no mesh
|
|
113
|
-
except:
|
|
114
|
-
res['note'] += '; factory_creation_failed'
|
|
115
|
-
factory = None
|
|
116
|
-
|
|
117
|
-
# Create the asset with or without factory
|
|
118
|
-
if factory:
|
|
119
|
-
asset = asset_tools.create_asset(
|
|
120
|
-
asset_name=name,
|
|
121
|
-
package_path=package_path,
|
|
122
|
-
asset_class=unreal.FoliageType_InstancedStaticMesh,
|
|
123
|
-
factory=factory
|
|
124
|
-
)
|
|
125
|
-
else:
|
|
126
|
-
# Try without factory
|
|
127
|
-
asset = asset_tools.create_asset(
|
|
128
|
-
asset_name=name,
|
|
129
|
-
package_path=package_path,
|
|
130
|
-
asset_class=unreal.FoliageType_InstancedStaticMesh,
|
|
131
|
-
factory=None
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if asset:
|
|
135
|
-
# Configure foliage properties
|
|
136
|
-
asset.set_editor_property('mesh', mesh)
|
|
137
|
-
if ${params.density !== undefined ? params.density : 1.0} >= 0:
|
|
138
|
-
asset.set_editor_property('density', ${params.density !== undefined ? params.density : 1.0})
|
|
139
|
-
if ${params.randomYaw === false ? 'False' : 'True'}:
|
|
140
|
-
asset.set_editor_property('random_yaw', True)
|
|
141
|
-
if ${params.alignToNormal === false ? 'False' : 'True'}:
|
|
142
|
-
asset.set_editor_property('align_to_normal', True)
|
|
143
|
-
|
|
144
|
-
# Set scale range
|
|
145
|
-
min_scale = ${params.minScale || 0.8}
|
|
146
|
-
max_scale = ${params.maxScale || 1.2}
|
|
147
|
-
asset.set_editor_property('scale_x', (min_scale, max_scale))
|
|
148
|
-
asset.set_editor_property('scale_y', (min_scale, max_scale))
|
|
149
|
-
asset.set_editor_property('scale_z', (min_scale, max_scale))
|
|
150
|
-
|
|
151
|
-
res['note'] += '; created_with_factory'
|
|
152
|
-
else:
|
|
153
|
-
res['note'] += '; factory_creation_failed'
|
|
154
|
-
except AttributeError:
|
|
155
|
-
# Fallback if factory doesn't exist - use base FoliageType
|
|
156
|
-
try:
|
|
157
|
-
asset = asset_tools.create_asset(
|
|
158
|
-
asset_name=name,
|
|
159
|
-
package_path=package_path,
|
|
160
|
-
asset_class=unreal.FoliageType,
|
|
161
|
-
factory=None
|
|
162
|
-
)
|
|
163
|
-
if asset:
|
|
164
|
-
res['note'] += '; created_base_foliage_type'
|
|
165
|
-
except Exception as e2:
|
|
166
|
-
res['note'] += f"; base_creation_failed: {e2}"
|
|
167
|
-
except Exception as e:
|
|
168
|
-
res['note'] += f"; factory_creation_failed: {e}"
|
|
169
|
-
asset = None
|
|
170
|
-
except Exception as e:
|
|
171
|
-
res['note'] += f"; create_asset failed: {e}"
|
|
172
|
-
asset = None
|
|
173
|
-
|
|
174
|
-
if asset and mesh:
|
|
175
|
-
try:
|
|
176
|
-
# Set the mesh property (different property names in different UE versions)
|
|
177
|
-
try:
|
|
178
|
-
asset.set_editor_property('mesh', mesh)
|
|
179
|
-
except:
|
|
180
|
-
try:
|
|
181
|
-
asset.set_editor_property('static_mesh', mesh)
|
|
182
|
-
except:
|
|
183
|
-
pass
|
|
184
|
-
|
|
185
|
-
# Save the asset
|
|
186
|
-
unreal.EditorAssetLibrary.save_asset(asset.get_path_name())
|
|
187
|
-
res['asset_path'] = str(asset.get_path_name())
|
|
188
|
-
res['created'] = True
|
|
189
|
-
res['method'] = 'FoliageType_InstancedStaticMesh'
|
|
190
|
-
except Exception as e:
|
|
191
|
-
res['note'] += f"; set/save asset failed: {e}"
|
|
192
|
-
elif not asset:
|
|
193
|
-
res['note'] += "; asset creation returned None"
|
|
194
|
-
elif not mesh:
|
|
195
|
-
res['note'] += "; mesh object is None, cannot assign to foliage type"
|
|
196
|
-
|
|
197
|
-
# Verify existence
|
|
198
|
-
res['exists_after'] = unreal.EditorAssetLibrary.does_asset_exist(res['asset_path']) if res['asset_path'] else False
|
|
199
|
-
res['success'] = res['exists_after'] or res['created']
|
|
200
|
-
|
|
201
|
-
except Exception as e:
|
|
202
|
-
res['success'] = False
|
|
203
|
-
res['note'] += f"; fatal: {e}"
|
|
204
|
-
|
|
205
|
-
print('RESULT:' + json.dumps(res))
|
|
206
|
-
`.trim();
|
|
207
|
-
|
|
208
|
-
const pyResp = await this.bridge.executePython(py);
|
|
209
|
-
const interpreted = interpretStandardResult(pyResp, {
|
|
210
|
-
successMessage: `Foliage type '${name}' processed`,
|
|
211
|
-
failureMessage: 'Add foliage type failed'
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
if (!interpreted.success) {
|
|
55
|
+
if (!this.automationBridge) {
|
|
56
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const base = meshPath.includes('.') ? meshPath : `${meshPath}.${meshPath.split('/').filter(Boolean).pop()}`;
|
|
61
|
+
const response = await this.automationBridge.sendAutomationRequest('add_foliage_type', {
|
|
62
|
+
name,
|
|
63
|
+
meshPath: base,
|
|
64
|
+
density: params.density ?? 100,
|
|
65
|
+
radius: params.radius ?? 0,
|
|
66
|
+
minScale: params.minScale ?? 1.0,
|
|
67
|
+
maxScale: params.maxScale ?? 1.0,
|
|
68
|
+
alignToNormal: params.alignToNormal ?? true,
|
|
69
|
+
randomYaw: params.randomYaw ?? true,
|
|
70
|
+
groundSlope: params.groundSlope ?? 45
|
|
71
|
+
}, {
|
|
72
|
+
timeoutMs: 60000
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (response.success === false) {
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: response.error || response.message || 'Add foliage type failed',
|
|
79
|
+
note: coerceString((response.result as any)?.note)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const payload = response.result as Record<string, unknown>;
|
|
84
|
+
const created = coerceBoolean(payload.created, false) ?? false;
|
|
85
|
+
const exists = coerceBoolean(payload.exists_after, false) ?? created;
|
|
86
|
+
const method = coerceString(payload.method) ?? 'Unknown';
|
|
87
|
+
const assetPath = coerceString(payload.asset_path);
|
|
88
|
+
const usedMesh = coerceString(payload.used_mesh);
|
|
89
|
+
const note = coerceString(payload.note);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
created,
|
|
94
|
+
exists,
|
|
95
|
+
method,
|
|
96
|
+
assetPath,
|
|
97
|
+
usedMesh,
|
|
98
|
+
note,
|
|
99
|
+
message: exists
|
|
100
|
+
? `Foliage type '${name}' ready (${method})`
|
|
101
|
+
: `Created foliage '${name}' but verification did not find it yet`
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
215
104
|
return {
|
|
216
105
|
success: false,
|
|
217
|
-
error:
|
|
218
|
-
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
106
|
+
error: `Failed to add foliage type: ${error instanceof Error ? error.message : String(error)}`
|
|
219
107
|
};
|
|
220
108
|
}
|
|
221
|
-
|
|
222
|
-
const payload = interpreted.payload as Record<string, unknown>;
|
|
223
|
-
const created = coerceBoolean(payload.created, false) ?? false;
|
|
224
|
-
const exists = coerceBoolean(payload.exists_after, false) ?? created;
|
|
225
|
-
const method = coerceString(payload.method) ?? 'Unknown';
|
|
226
|
-
const assetPath = coerceString(payload.asset_path);
|
|
227
|
-
const usedMesh = coerceString(payload.used_mesh);
|
|
228
|
-
const note = coerceString(payload.note);
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
success: true,
|
|
232
|
-
created,
|
|
233
|
-
exists,
|
|
234
|
-
method,
|
|
235
|
-
assetPath,
|
|
236
|
-
usedMesh,
|
|
237
|
-
note,
|
|
238
|
-
message: exists
|
|
239
|
-
? `Foliage type '${name}' ready (${method})`
|
|
240
|
-
: `Created foliage '${name}' but verification did not find it yet`
|
|
241
|
-
};
|
|
242
109
|
}
|
|
243
110
|
|
|
244
111
|
// Paint foliage by placing HISM instances (editor-only)
|
|
@@ -251,7 +118,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
251
118
|
}) {
|
|
252
119
|
const errors: string[] = [];
|
|
253
120
|
const foliageType = String(params?.foliageType ?? '').trim();
|
|
254
|
-
const pos = Array.isArray(params?.position) ? params.position : [0,0,0];
|
|
121
|
+
const pos = Array.isArray(params?.position) ? params.position : [0, 0, 0];
|
|
255
122
|
|
|
256
123
|
if (!foliageType || foliageType.toLowerCase() === 'undefined' || foliageType.toLowerCase() === 'any') {
|
|
257
124
|
errors.push(`Invalid foliageType: '${params?.foliageType}'`);
|
|
@@ -274,133 +141,94 @@ print('RESULT:' + json.dumps(res))
|
|
|
274
141
|
return { success: false, error: errors.join('; ') };
|
|
275
142
|
}
|
|
276
143
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
container.set_actor_label(label)
|
|
313
|
-
except Exception:
|
|
314
|
-
pass
|
|
315
|
-
|
|
316
|
-
# Resolve mesh from FoliageType asset
|
|
317
|
-
mesh = None
|
|
318
|
-
fol_asset_path = f"/Game/Foliage/Types/{foliage_type_name}.{foliage_type_name}"
|
|
319
|
-
if unreal.EditorAssetLibrary.does_asset_exist(fol_asset_path):
|
|
320
|
-
try:
|
|
321
|
-
ft_asset = unreal.EditorAssetLibrary.load_asset(fol_asset_path)
|
|
322
|
-
mesh = ft_asset.get_editor_property('mesh')
|
|
323
|
-
except Exception:
|
|
324
|
-
mesh = None
|
|
325
|
-
|
|
326
|
-
if not mesh:
|
|
327
|
-
mesh = unreal.EditorAssetLibrary.load_asset('/Engine/EngineMeshes/Sphere')
|
|
328
|
-
res['note'] += '; used_fallback_mesh'
|
|
329
|
-
|
|
330
|
-
if mesh:
|
|
331
|
-
res['used_mesh'] = str(mesh.get_path_name())
|
|
332
|
-
|
|
333
|
-
# Since HISM components and add_component don't work in this version,
|
|
334
|
-
# spawn individual StaticMeshActors for each instance
|
|
335
|
-
target_count = max(5, int(radius / 20.0))
|
|
336
|
-
added = 0
|
|
337
|
-
for i in range(target_count):
|
|
338
|
-
ang = random.random() * math.tau
|
|
339
|
-
r = random.random() * radius
|
|
340
|
-
x, y, z = px + math.cos(ang) * r, py + math.sin(ang) * r, pz
|
|
341
|
-
try:
|
|
342
|
-
# Spawn static mesh actor at position using modern subsystem
|
|
343
|
-
inst_actor = actor_subsystem.spawn_actor_from_class(
|
|
344
|
-
unreal.StaticMeshActor,
|
|
345
|
-
unreal.Vector(x, y, z),
|
|
346
|
-
unreal.Rotator(0, random.random()*360.0, 0)
|
|
347
|
-
)
|
|
348
|
-
if inst_actor and mesh:
|
|
349
|
-
# Set mesh on the actor's component
|
|
350
|
-
try:
|
|
351
|
-
mesh_comp = inst_actor.static_mesh_component
|
|
352
|
-
if mesh_comp:
|
|
353
|
-
mesh_comp.set_static_mesh(mesh)
|
|
354
|
-
inst_actor.set_actor_label(f"{foliage_type_name}_instance_{i}")
|
|
355
|
-
# Group under the container for organization
|
|
356
|
-
inst_actor.attach_to_actor(container, "", unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False)
|
|
357
|
-
added += 1
|
|
358
|
-
except Exception as e:
|
|
359
|
-
res['note'] += f"; instance_{i} setup failed: {e}"
|
|
360
|
-
except Exception as e:
|
|
361
|
-
res['note'] += f"; spawn instance_{i} failed: {e}"
|
|
362
|
-
|
|
363
|
-
res['added'] = added
|
|
364
|
-
res['actor'] = container.get_actor_label()
|
|
365
|
-
res['component'] = 'StaticMeshActors' # Using actors instead of components
|
|
366
|
-
res['success'] = True
|
|
367
|
-
except Exception as e:
|
|
368
|
-
res['success'] = False
|
|
369
|
-
res['note'] += f"; fatal: {e}"
|
|
370
|
-
|
|
371
|
-
print('RESULT:' + json.dumps(res))
|
|
372
|
-
`.trim();
|
|
373
|
-
|
|
374
|
-
const pyResp = await this.bridge.executePython(py);
|
|
375
|
-
const interpreted = interpretStandardResult(pyResp, {
|
|
376
|
-
successMessage: `Painted foliage for '${foliageType}'`,
|
|
377
|
-
failureMessage: 'Paint foliage failed'
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
if (!interpreted.success) {
|
|
144
|
+
if (!this.automationBridge) {
|
|
145
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const typePath = foliageType.includes('/') ? foliageType : `/Game/Foliage/${foliageType}.${foliageType}`;
|
|
150
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_foliage', {
|
|
151
|
+
foliageTypePath: typePath,
|
|
152
|
+
locations: [{ x: pos[0], y: pos[1], z: pos[2] }],
|
|
153
|
+
brushSize: Number.isFinite(params.brushSize as number) ? (params.brushSize as number) : 300,
|
|
154
|
+
paintDensity: params.paintDensity,
|
|
155
|
+
eraseMode: params.eraseMode
|
|
156
|
+
}, {
|
|
157
|
+
timeoutMs: 60000
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (response.success === false) {
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
error: response.error || response.message || 'Paint foliage failed',
|
|
164
|
+
note: coerceString((response.result as any)?.note)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const payload = response.result as Record<string, unknown>;
|
|
169
|
+
const added = coerceNumber(payload.instancesPlaced) ?? coerceNumber((payload as any)?.count) ?? 0;
|
|
170
|
+
const note = coerceString(payload.note);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
success: true,
|
|
174
|
+
added,
|
|
175
|
+
note,
|
|
176
|
+
message: `Painted ${added} instances for '${foliageType}' around (${pos[0]}, ${pos[1]}, ${pos[2]})`
|
|
177
|
+
};
|
|
178
|
+
} catch (error) {
|
|
381
179
|
return {
|
|
382
180
|
success: false,
|
|
383
|
-
error:
|
|
384
|
-
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
181
|
+
error: `Failed to paint foliage: ${error instanceof Error ? error.message : String(error)}`
|
|
385
182
|
};
|
|
386
183
|
}
|
|
184
|
+
}
|
|
387
185
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
186
|
+
// Query foliage instances (plugin-native)
|
|
187
|
+
async getFoliageInstances(params: { foliageType?: string }) {
|
|
188
|
+
if (!this.automationBridge) {
|
|
189
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const typePath = params.foliageType ? (params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`) : undefined;
|
|
193
|
+
const response = await this.automationBridge.sendAutomationRequest('get_foliage_instances', {
|
|
194
|
+
foliageTypePath: typePath
|
|
195
|
+
}, { timeoutMs: 60000 });
|
|
196
|
+
if (response.success === false) {
|
|
197
|
+
return { success: false, error: response.error || response.message || 'Get foliage instances failed' };
|
|
198
|
+
}
|
|
199
|
+
const payload = response.result as Record<string, unknown>;
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
count: coerceNumber(payload.count) ?? 0,
|
|
203
|
+
instances: (payload.instances as any[]) ?? []
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
return { success: false, error: `Failed to get foliage instances: ${error instanceof Error ? error.message : String(error)}` };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
394
209
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
210
|
+
// Remove foliage (plugin-native)
|
|
211
|
+
async removeFoliage(params: { foliageType?: string; removeAll?: boolean }) {
|
|
212
|
+
if (!this.automationBridge) {
|
|
213
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const typePath = params.foliageType ? (params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`) : undefined;
|
|
217
|
+
const response = await this.automationBridge.sendAutomationRequest('remove_foliage', {
|
|
218
|
+
foliageTypePath: typePath,
|
|
219
|
+
removeAll: !!params.removeAll
|
|
220
|
+
}, { timeoutMs: 60000 });
|
|
221
|
+
if (response.success === false) {
|
|
222
|
+
return { success: false, error: response.error || response.message || 'Remove foliage failed' };
|
|
223
|
+
}
|
|
224
|
+
const payload = response.result as Record<string, unknown>;
|
|
225
|
+
return {
|
|
226
|
+
success: true,
|
|
227
|
+
instancesRemoved: coerceNumber(payload.instancesRemoved) ?? 0
|
|
228
|
+
};
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return { success: false, error: `Failed to remove foliage: ${error instanceof Error ? error.message : String(error)}` };
|
|
231
|
+
}
|
|
404
232
|
}
|
|
405
233
|
|
|
406
234
|
// Create instanced mesh
|
|
@@ -415,26 +243,26 @@ print('RESULT:' + json.dumps(res))
|
|
|
415
243
|
enableCulling?: boolean;
|
|
416
244
|
cullDistance?: number;
|
|
417
245
|
}) {
|
|
418
|
-
|
|
419
|
-
|
|
246
|
+
const commands: string[] = [];
|
|
247
|
+
|
|
420
248
|
commands.push(`CreateInstancedStaticMesh ${params.name} ${params.meshPath}`);
|
|
421
|
-
|
|
249
|
+
|
|
422
250
|
for (const instance of params.instances) {
|
|
423
251
|
const rot = instance.rotation || [0, 0, 0];
|
|
424
252
|
const scale = instance.scale || [1, 1, 1];
|
|
425
253
|
commands.push(`AddInstance ${params.name} ${instance.position.join(' ')} ${rot.join(' ')} ${scale.join(' ')}`);
|
|
426
254
|
}
|
|
427
|
-
|
|
255
|
+
|
|
428
256
|
if (params.enableCulling !== undefined) {
|
|
429
257
|
commands.push(`SetInstanceCulling ${params.name} ${params.enableCulling}`);
|
|
430
258
|
}
|
|
431
|
-
|
|
259
|
+
|
|
432
260
|
if (params.cullDistance !== undefined) {
|
|
433
261
|
commands.push(`SetInstanceCullDistance ${params.name} ${params.cullDistance}`);
|
|
434
262
|
}
|
|
435
|
-
|
|
263
|
+
|
|
436
264
|
await this.bridge.executeConsoleCommands(commands);
|
|
437
|
-
|
|
265
|
+
|
|
438
266
|
return { success: true, message: `Instanced mesh ${params.name} created with ${params.instances.length} instances` };
|
|
439
267
|
}
|
|
440
268
|
|
|
@@ -444,51 +272,158 @@ print('RESULT:' + json.dumps(res))
|
|
|
444
272
|
lodDistances?: number[];
|
|
445
273
|
screenSize?: number[];
|
|
446
274
|
}) {
|
|
447
|
-
|
|
448
|
-
|
|
275
|
+
const commands: string[] = [];
|
|
276
|
+
|
|
449
277
|
if (params.lodDistances) {
|
|
450
278
|
commands.push(`SetFoliageLODDistances ${params.foliageType} ${params.lodDistances.join(' ')}`);
|
|
451
279
|
}
|
|
452
|
-
|
|
280
|
+
|
|
453
281
|
if (params.screenSize) {
|
|
454
282
|
commands.push(`SetFoliageLODScreenSize ${params.foliageType} ${params.screenSize.join(' ')}`);
|
|
455
283
|
}
|
|
456
|
-
|
|
284
|
+
|
|
457
285
|
await this.bridge.executeConsoleCommands(commands);
|
|
458
|
-
|
|
286
|
+
|
|
459
287
|
return { success: true, message: 'Foliage LOD settings updated' };
|
|
460
288
|
}
|
|
461
289
|
|
|
290
|
+
// Alias for addFoliageType to match interface/handler usage
|
|
291
|
+
async addFoliage(params: { foliageType: string; locations: Array<{ x: number; y: number; z: number }> }) {
|
|
292
|
+
// Delegate to paintFoliage which handles placing instances at locations
|
|
293
|
+
if (params.locations && params.locations.length > 0) {
|
|
294
|
+
if (!this.automationBridge) {
|
|
295
|
+
throw new Error('Automation Bridge not available.');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_foliage', {
|
|
299
|
+
foliageTypePath: params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`,
|
|
300
|
+
locations: params.locations,
|
|
301
|
+
brushSize: 0, // Exact placement
|
|
302
|
+
paintDensity: 1,
|
|
303
|
+
eraseMode: false
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (!response.success) {
|
|
307
|
+
return { success: false, error: response.error || 'Failed to add foliage instances' };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return { success: true, message: `Added ${params.locations.length} foliage instances` };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return { success: true, message: 'No locations provided for addFoliage' };
|
|
314
|
+
}
|
|
315
|
+
|
|
462
316
|
// Create procedural foliage
|
|
463
317
|
async createProceduralFoliage(params: {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
318
|
+
name: string;
|
|
319
|
+
bounds?: { location: { x: number; y: number; z: number }; size: { x: number; y: number; z: number } };
|
|
320
|
+
foliageTypes?: Array<{
|
|
321
|
+
meshPath: string;
|
|
322
|
+
density: number;
|
|
323
|
+
minScale?: number;
|
|
324
|
+
maxScale?: number;
|
|
325
|
+
alignToNormal?: boolean;
|
|
326
|
+
randomYaw?: boolean;
|
|
327
|
+
}>;
|
|
328
|
+
// Legacy params compatibility
|
|
329
|
+
volumeName?: string;
|
|
330
|
+
position?: [number, number, number];
|
|
331
|
+
size?: [number, number, number];
|
|
468
332
|
seed?: number;
|
|
469
333
|
tileSize?: number;
|
|
470
334
|
}) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
335
|
+
if (!this.automationBridge) {
|
|
336
|
+
throw new Error('Automation Bridge not available.');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const volName = params.volumeName || params.name || 'ProceduralFoliageVolume';
|
|
340
|
+
const loc = params.bounds?.location ? [params.bounds.location.x, params.bounds.location.y, params.bounds.location.z] : (params.position || [0, 0, 0]);
|
|
341
|
+
const size = params.bounds?.size ? [params.bounds.size.x, params.bounds.size.y, params.bounds.size.z] : (params.size || [1000, 1000, 100]);
|
|
342
|
+
|
|
343
|
+
// Normalize foliage types from both formats
|
|
344
|
+
const foliageTypes = Array.isArray(params.foliageTypes)
|
|
345
|
+
? params.foliageTypes.map(t => {
|
|
346
|
+
if (typeof t === 'string') return { meshPath: t, density: 0.5 };
|
|
347
|
+
return t;
|
|
348
|
+
})
|
|
349
|
+
: [];
|
|
350
|
+
|
|
351
|
+
const payload = {
|
|
352
|
+
name: volName,
|
|
353
|
+
bounds: {
|
|
354
|
+
location: { x: loc[0], y: loc[1], z: loc[2] },
|
|
355
|
+
size: { x: size[0], y: size[1], z: size[2] }
|
|
356
|
+
},
|
|
357
|
+
foliageTypes,
|
|
358
|
+
seed: params.seed ?? 42,
|
|
359
|
+
tileSize: params.tileSize ?? 1000
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const response = await this.automationBridge.sendAutomationRequest('create_procedural_foliage', payload);
|
|
363
|
+
|
|
364
|
+
if (!response.success) {
|
|
365
|
+
return {
|
|
366
|
+
success: false,
|
|
367
|
+
error: response.error || 'Failed to create procedural foliage'
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const result = response.result as any;
|
|
372
|
+
return {
|
|
373
|
+
success: true,
|
|
374
|
+
message: `Procedural foliage volume ${volName} created`,
|
|
375
|
+
details: response,
|
|
376
|
+
volumeActor: result?.volume_actor,
|
|
377
|
+
spawnerPath: result?.spawner_path,
|
|
378
|
+
foliageTypesCount: result?.foliage_types_count
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Add foliage instances using InstancedFoliageActor
|
|
384
|
+
* Direct instance placement approach
|
|
385
|
+
*/
|
|
386
|
+
async addFoliageInstances(params: {
|
|
387
|
+
foliageType: string; // Path to FoliageType or mesh
|
|
388
|
+
transforms: Array<{
|
|
389
|
+
location: [number, number, number];
|
|
390
|
+
rotation?: [number, number, number];
|
|
391
|
+
scale?: [number, number, number];
|
|
392
|
+
}>;
|
|
393
|
+
}) {
|
|
394
|
+
if (!this.automationBridge) {
|
|
395
|
+
throw new Error('Automation Bridge not available. Foliage instance placement requires plugin support.');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
const typePath = params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`;
|
|
400
|
+
const response = await this.automationBridge.sendAutomationRequest('add_foliage_instances', {
|
|
401
|
+
foliageType: typePath,
|
|
402
|
+
transforms: params.transforms
|
|
403
|
+
}, {
|
|
404
|
+
timeoutMs: 120000 // 2 minutes for instance placement
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
if (response.success === false) {
|
|
408
|
+
return {
|
|
409
|
+
success: false,
|
|
410
|
+
error: response.error || response.message || 'Failed to add foliage instances',
|
|
411
|
+
message: response.message || 'Failed to add foliage instances'
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const result = response.result as any;
|
|
416
|
+
return {
|
|
417
|
+
success: true,
|
|
418
|
+
message: response.message || `Added ${result?.instances_count || params.transforms.length} foliage instances`,
|
|
419
|
+
instancesCount: result?.instances_count
|
|
420
|
+
};
|
|
421
|
+
} catch (error) {
|
|
422
|
+
return {
|
|
423
|
+
success: false,
|
|
424
|
+
error: `Failed to add foliage instances: ${error instanceof Error ? error.message : String(error)}`
|
|
425
|
+
};
|
|
426
|
+
}
|
|
492
427
|
}
|
|
493
428
|
|
|
494
429
|
// Set foliage collision
|
|
@@ -498,22 +433,22 @@ print('RESULT:' + json.dumps(res))
|
|
|
498
433
|
collisionProfile?: string;
|
|
499
434
|
generateOverlapEvents?: boolean;
|
|
500
435
|
}) {
|
|
501
|
-
|
|
502
|
-
|
|
436
|
+
const commands: string[] = [];
|
|
437
|
+
|
|
503
438
|
if (params.collisionEnabled !== undefined) {
|
|
504
439
|
commands.push(`SetFoliageCollision ${params.foliageType} ${params.collisionEnabled}`);
|
|
505
440
|
}
|
|
506
|
-
|
|
441
|
+
|
|
507
442
|
if (params.collisionProfile) {
|
|
508
443
|
commands.push(`SetFoliageCollisionProfile ${params.foliageType} ${params.collisionProfile}`);
|
|
509
444
|
}
|
|
510
|
-
|
|
445
|
+
|
|
511
446
|
if (params.generateOverlapEvents !== undefined) {
|
|
512
447
|
commands.push(`SetFoliageOverlapEvents ${params.foliageType} ${params.generateOverlapEvents}`);
|
|
513
448
|
}
|
|
514
|
-
|
|
449
|
+
|
|
515
450
|
await this.bridge.executeConsoleCommands(commands);
|
|
516
|
-
|
|
451
|
+
|
|
517
452
|
return { success: true, message: 'Foliage collision settings updated' };
|
|
518
453
|
}
|
|
519
454
|
|
|
@@ -529,26 +464,26 @@ print('RESULT:' + json.dumps(res))
|
|
|
529
464
|
windStrength?: number;
|
|
530
465
|
windSpeed?: number;
|
|
531
466
|
}) {
|
|
532
|
-
|
|
533
|
-
|
|
467
|
+
const commands: string[] = [];
|
|
468
|
+
|
|
534
469
|
commands.push(`CreateGrassSystem ${params.name}`);
|
|
535
|
-
|
|
470
|
+
|
|
536
471
|
for (const grassType of params.grassTypes) {
|
|
537
472
|
const minScale = grassType.minScale || 0.8;
|
|
538
473
|
const maxScale = grassType.maxScale || 1.2;
|
|
539
474
|
commands.push(`AddGrassType ${params.name} ${grassType.meshPath} ${grassType.density} ${minScale} ${maxScale}`);
|
|
540
475
|
}
|
|
541
|
-
|
|
476
|
+
|
|
542
477
|
if (params.windStrength !== undefined) {
|
|
543
478
|
commands.push(`SetGrassWindStrength ${params.name} ${params.windStrength}`);
|
|
544
479
|
}
|
|
545
|
-
|
|
480
|
+
|
|
546
481
|
if (params.windSpeed !== undefined) {
|
|
547
482
|
commands.push(`SetGrassWindSpeed ${params.name} ${params.windSpeed}`);
|
|
548
483
|
}
|
|
549
|
-
|
|
484
|
+
|
|
550
485
|
await this.bridge.executeConsoleCommands(commands);
|
|
551
|
-
|
|
486
|
+
|
|
552
487
|
return { success: true, message: `Grass system ${params.name} created` };
|
|
553
488
|
}
|
|
554
489
|
|
|
@@ -570,7 +505,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
570
505
|
selectAll?: boolean;
|
|
571
506
|
}) {
|
|
572
507
|
let command: string;
|
|
573
|
-
|
|
508
|
+
|
|
574
509
|
if (params.selectAll) {
|
|
575
510
|
command = `SelectAllFoliage ${params.foliageType}`;
|
|
576
511
|
} else if (params.position && params.radius) {
|
|
@@ -578,7 +513,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
578
513
|
} else {
|
|
579
514
|
command = `SelectFoliageType ${params.foliageType}`;
|
|
580
515
|
}
|
|
581
|
-
|
|
516
|
+
|
|
582
517
|
return this.bridge.executeConsoleCommand(command);
|
|
583
518
|
}
|
|
584
519
|
|
|
@@ -589,20 +524,20 @@ print('RESULT:' + json.dumps(res))
|
|
|
589
524
|
updateMesh?: boolean;
|
|
590
525
|
newMeshPath?: string;
|
|
591
526
|
}) {
|
|
592
|
-
|
|
593
|
-
|
|
527
|
+
const commands: string[] = [];
|
|
528
|
+
|
|
594
529
|
if (params.updateTransforms) {
|
|
595
530
|
commands.push(`UpdateFoliageTransforms ${params.foliageType}`);
|
|
596
531
|
}
|
|
597
|
-
|
|
532
|
+
|
|
598
533
|
if (params.updateMesh && params.newMeshPath) {
|
|
599
534
|
commands.push(`UpdateFoliageMesh ${params.foliageType} ${params.newMeshPath}`);
|
|
600
535
|
}
|
|
601
|
-
|
|
536
|
+
|
|
602
537
|
commands.push(`RefreshFoliage ${params.foliageType}`);
|
|
603
|
-
|
|
538
|
+
|
|
604
539
|
await this.bridge.executeConsoleCommands(commands);
|
|
605
|
-
|
|
540
|
+
|
|
606
541
|
return { success: true, message: 'Foliage instances updated' };
|
|
607
542
|
}
|
|
608
543
|
|
|
@@ -612,18 +547,18 @@ print('RESULT:' + json.dumps(res))
|
|
|
612
547
|
spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All';
|
|
613
548
|
excludeAreas?: Array<[number, number, number, number]>; // [x, y, z, radius]
|
|
614
549
|
}) {
|
|
615
|
-
|
|
616
|
-
|
|
550
|
+
const commands: string[] = [];
|
|
551
|
+
|
|
617
552
|
commands.push(`CreateFoliageSpawner ${params.name} ${params.spawnArea}`);
|
|
618
|
-
|
|
553
|
+
|
|
619
554
|
if (params.excludeAreas) {
|
|
620
555
|
for (const area of params.excludeAreas) {
|
|
621
556
|
commands.push(`AddFoliageExclusionArea ${params.name} ${area.join(' ')}`);
|
|
622
557
|
}
|
|
623
558
|
}
|
|
624
|
-
|
|
559
|
+
|
|
625
560
|
await this.bridge.executeConsoleCommands(commands);
|
|
626
|
-
|
|
561
|
+
|
|
627
562
|
return { success: true, message: `Foliage spawner ${params.name} created` };
|
|
628
563
|
}
|
|
629
564
|
|
|
@@ -635,24 +570,24 @@ print('RESULT:' + json.dumps(res))
|
|
|
635
570
|
reduceDrawCalls?: boolean;
|
|
636
571
|
}) {
|
|
637
572
|
const commands = [];
|
|
638
|
-
|
|
573
|
+
|
|
639
574
|
if (params.mergeInstances) {
|
|
640
575
|
commands.push('MergeFoliageInstances');
|
|
641
576
|
}
|
|
642
|
-
|
|
577
|
+
|
|
643
578
|
if (params.generateClusters) {
|
|
644
579
|
const size = params.clusterSize || 100;
|
|
645
580
|
commands.push(`GenerateFoliageClusters ${size}`);
|
|
646
581
|
}
|
|
647
|
-
|
|
582
|
+
|
|
648
583
|
if (params.reduceDrawCalls) {
|
|
649
584
|
commands.push('OptimizeFoliageDrawCalls');
|
|
650
585
|
}
|
|
651
|
-
|
|
586
|
+
|
|
652
587
|
commands.push('RebuildFoliageTree');
|
|
653
|
-
|
|
588
|
+
|
|
654
589
|
await this.bridge.executeConsoleCommands(commands);
|
|
655
|
-
|
|
590
|
+
|
|
656
591
|
return { success: true, message: 'Foliage optimized' };
|
|
657
592
|
}
|
|
658
593
|
}
|