unreal-engine-mcp-server 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +26 -0
- package/.env.production +38 -7
- package/.eslintrc.json +0 -54
- package/.eslintrc.override.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- package/.github/copilot-instructions.md +478 -45
- package/.github/dependabot.yml +19 -0
- package/.github/labeler.yml +24 -0
- package/.github/labels.yml +70 -0
- package/.github/pull_request_template.md +42 -0
- package/.github/release-drafter.yml +148 -0
- package/.github/workflows/auto-merge.yml +38 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/dependency-review.yml +17 -0
- package/.github/workflows/gemini-issue-triage.yml +172 -0
- package/.github/workflows/greetings.yml +23 -0
- package/.github/workflows/labeler.yml +16 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +12 -7
- package/.github/workflows/release-drafter.yml +23 -0
- package/.github/workflows/release.yml +112 -0
- package/.github/workflows/semantic-pull-request.yml +35 -0
- package/.github/workflows/smoke-test.yml +36 -0
- package/.github/workflows/stale.yml +28 -0
- package/CHANGELOG.md +267 -31
- package/CONTRIBUTING.md +140 -0
- package/README.md +166 -71
- package/claude_desktop_config_example.json +7 -6
- package/dist/automation/bridge.d.ts +50 -0
- package/dist/automation/bridge.js +452 -0
- package/dist/automation/connection-manager.d.ts +23 -0
- package/dist/automation/connection-manager.js +107 -0
- package/dist/automation/handshake.d.ts +11 -0
- package/dist/automation/handshake.js +89 -0
- package/dist/automation/index.d.ts +3 -0
- package/dist/automation/index.js +3 -0
- package/dist/automation/message-handler.d.ts +12 -0
- package/dist/automation/message-handler.js +149 -0
- package/dist/automation/request-tracker.d.ts +25 -0
- package/dist/automation/request-tracker.js +98 -0
- package/dist/automation/types.d.ts +130 -0
- package/dist/automation/types.js +2 -0
- package/dist/cli.js +32 -5
- package/dist/config.d.ts +27 -0
- package/dist/config.js +60 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +12 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +743 -0
- package/dist/graphql/schema.d.ts +5 -0
- package/dist/graphql/schema.js +437 -0
- package/dist/graphql/server.d.ts +26 -0
- package/dist/graphql/server.js +115 -0
- package/dist/graphql/types.d.ts +7 -0
- package/dist/graphql/types.js +2 -0
- package/dist/handlers/resource-handlers.d.ts +20 -0
- package/dist/handlers/resource-handlers.js +180 -0
- package/dist/index.d.ts +31 -18
- package/dist/index.js +119 -619
- package/dist/prompts/index.js +4 -4
- package/dist/resources/actors.d.ts +17 -12
- package/dist/resources/actors.js +56 -76
- package/dist/resources/assets.d.ts +6 -14
- package/dist/resources/assets.js +115 -147
- package/dist/resources/levels.d.ts +13 -13
- package/dist/resources/levels.js +25 -34
- package/dist/server/resource-registry.d.ts +20 -0
- package/dist/server/resource-registry.js +37 -0
- package/dist/server/tool-registry.d.ts +23 -0
- package/dist/server/tool-registry.js +322 -0
- package/dist/server-setup.d.ts +21 -0
- package/dist/server-setup.js +111 -0
- package/dist/services/health-monitor.d.ts +34 -0
- package/dist/services/health-monitor.js +105 -0
- package/dist/services/metrics-server.d.ts +11 -0
- package/dist/services/metrics-server.js +105 -0
- package/dist/tools/actors.d.ts +147 -9
- package/dist/tools/actors.js +350 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +117 -19
- package/dist/tools/assets.js +259 -284
- package/dist/tools/audio.d.ts +102 -42
- package/dist/tools/audio.js +272 -685
- package/dist/tools/base-tool.d.ts +17 -0
- package/dist/tools/base-tool.js +46 -0
- package/dist/tools/behavior-tree.d.ts +94 -0
- package/dist/tools/behavior-tree.js +39 -0
- package/dist/tools/blueprint/helpers.d.ts +29 -0
- package/dist/tools/blueprint/helpers.js +182 -0
- package/dist/tools/blueprint.d.ts +228 -118
- package/dist/tools/blueprint.js +685 -832
- package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
- package/dist/tools/consolidated-tool-definitions.js +829 -496
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +211 -1026
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +11 -0
- package/dist/tools/dynamic-handler-registry.js +101 -0
- package/dist/tools/editor.d.ts +139 -18
- package/dist/tools/editor.js +239 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +36 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +105 -14
- package/dist/tools/foliage.js +219 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +232 -0
- package/dist/tools/handlers/animation-handlers.d.ts +3 -0
- package/dist/tools/handlers/animation-handlers.js +185 -0
- package/dist/tools/handlers/argument-helper.d.ts +16 -0
- package/dist/tools/handlers/argument-helper.js +80 -0
- package/dist/tools/handlers/asset-handlers.d.ts +3 -0
- package/dist/tools/handlers/asset-handlers.js +496 -0
- package/dist/tools/handlers/audio-handlers.d.ts +3 -0
- package/dist/tools/handlers/audio-handlers.js +166 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
- package/dist/tools/handlers/blueprint-handlers.js +358 -0
- package/dist/tools/handlers/common-handlers.d.ts +14 -0
- package/dist/tools/handlers/common-handlers.js +56 -0
- package/dist/tools/handlers/editor-handlers.d.ts +3 -0
- package/dist/tools/handlers/editor-handlers.js +119 -0
- package/dist/tools/handlers/effect-handlers.d.ts +3 -0
- package/dist/tools/handlers/effect-handlers.js +171 -0
- package/dist/tools/handlers/environment-handlers.d.ts +3 -0
- package/dist/tools/handlers/environment-handlers.js +170 -0
- package/dist/tools/handlers/graph-handlers.d.ts +3 -0
- package/dist/tools/handlers/graph-handlers.js +90 -0
- package/dist/tools/handlers/input-handlers.d.ts +3 -0
- package/dist/tools/handlers/input-handlers.js +21 -0
- package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
- package/dist/tools/handlers/inspect-handlers.js +383 -0
- package/dist/tools/handlers/level-handlers.d.ts +3 -0
- package/dist/tools/handlers/level-handlers.js +237 -0
- package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
- package/dist/tools/handlers/lighting-handlers.js +144 -0
- package/dist/tools/handlers/performance-handlers.d.ts +3 -0
- package/dist/tools/handlers/performance-handlers.js +130 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
- package/dist/tools/handlers/pipeline-handlers.js +110 -0
- package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
- package/dist/tools/handlers/sequence-handlers.js +376 -0
- package/dist/tools/handlers/system-handlers.d.ts +4 -0
- package/dist/tools/handlers/system-handlers.js +506 -0
- package/dist/tools/input.d.ts +19 -0
- package/dist/tools/input.js +89 -0
- package/dist/tools/introspection.d.ts +103 -40
- package/dist/tools/introspection.js +425 -568
- package/dist/tools/landscape.d.ts +97 -36
- package/dist/tools/landscape.js +280 -409
- package/dist/tools/level.d.ts +130 -10
- package/dist/tools/level.js +639 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +441 -943
- package/dist/tools/logs.d.ts +3 -3
- package/dist/tools/logs.js +5 -57
- package/dist/tools/materials.d.ts +91 -24
- package/dist/tools/materials.js +190 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +232 -182
- package/dist/tools/performance.d.ts +27 -12
- package/dist/tools/performance.js +204 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +171 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +73 -48
- package/dist/tools/sequence.js +196 -748
- package/dist/tools/tool-definition-utils.d.ts +59 -0
- package/dist/tools/tool-definition-utils.js +35 -0
- package/dist/tools/ui.d.ts +66 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/tool-interfaces.d.ts +898 -0
- package/dist/types/tool-interfaces.js +2 -0
- package/dist/types/tool-types.d.ts +183 -19
- package/dist/types/tool-types.js +0 -4
- package/dist/unreal-bridge.d.ts +24 -131
- package/dist/unreal-bridge.js +364 -1506
- package/dist/utils/command-validator.d.ts +9 -0
- package/dist/utils/command-validator.js +67 -0
- package/dist/utils/elicitation.d.ts +1 -1
- package/dist/utils/elicitation.js +12 -15
- package/dist/utils/error-handler.d.ts +2 -51
- package/dist/utils/error-handler.js +11 -87
- package/dist/utils/ini-reader.d.ts +3 -0
- package/dist/utils/ini-reader.js +69 -0
- package/dist/utils/logger.js +9 -6
- package/dist/utils/normalize.d.ts +3 -0
- package/dist/utils/normalize.js +56 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +33 -0
- package/dist/utils/response-validator.d.ts +3 -24
- package/dist/utils/response-validator.js +130 -81
- package/dist/utils/result-helpers.d.ts +4 -5
- package/dist/utils/result-helpers.js +15 -16
- package/dist/utils/safe-json.js +5 -11
- package/dist/utils/unreal-command-queue.d.ts +24 -0
- package/dist/utils/unreal-command-queue.js +120 -0
- package/dist/utils/validation.d.ts +0 -40
- package/dist/utils/validation.js +1 -78
- package/dist/wasm/index.d.ts +70 -0
- package/dist/wasm/index.js +535 -0
- package/docs/GraphQL-API.md +888 -0
- package/docs/Migration-Guide-v0.5.0.md +692 -0
- package/docs/Roadmap.md +53 -0
- package/docs/WebAssembly-Integration.md +628 -0
- package/docs/editor-plugin-extension.md +370 -0
- package/docs/handler-mapping.md +242 -0
- package/docs/native-automation-progress.md +128 -0
- package/docs/testing-guide.md +423 -0
- package/mcp-config-example.json +6 -6
- package/package.json +60 -27
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
- package/scripts/check-unreal-connection.mjs +19 -0
- package/scripts/clean-tmp.js +23 -0
- package/scripts/patch-wasm.js +26 -0
- package/scripts/run-all-tests.mjs +131 -0
- package/scripts/smoke-test.ts +94 -0
- package/scripts/sync-mcp-plugin.js +143 -0
- package/scripts/test-no-plugin-alternates.mjs +113 -0
- package/scripts/validate-server.js +46 -0
- package/scripts/verify-automation-bridge.js +200 -0
- package/server.json +57 -21
- package/src/automation/bridge.ts +558 -0
- package/src/automation/connection-manager.ts +130 -0
- package/src/automation/handshake.ts +99 -0
- package/src/automation/index.ts +2 -0
- package/src/automation/message-handler.ts +167 -0
- package/src/automation/request-tracker.ts +123 -0
- package/src/automation/types.ts +107 -0
- package/src/cli.ts +33 -6
- package/src/config.ts +73 -0
- package/src/constants.ts +12 -0
- package/src/graphql/resolvers.ts +1010 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +154 -0
- package/src/graphql/types.ts +7 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +152 -663
- package/src/prompts/index.ts +4 -4
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +147 -134
- package/src/resources/levels.ts +28 -33
- package/src/server/resource-registry.ts +47 -0
- package/src/server/tool-registry.ts +354 -0
- package/src/server-setup.ts +148 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +417 -322
- package/src/tools/animation.ts +671 -461
- package/src/tools/assets.ts +353 -289
- package/src/tools/audio.ts +323 -766
- package/src/tools/base-tool.ts +52 -0
- package/src/tools/behavior-tree.ts +45 -0
- package/src/tools/blueprint/helpers.ts +189 -0
- package/src/tools/blueprint.ts +787 -965
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +272 -1139
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +151 -0
- package/src/tools/editor.ts +309 -246
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +287 -0
- package/src/tools/foliage.ts +314 -379
- package/src/tools/handlers/actor-handlers.ts +271 -0
- package/src/tools/handlers/animation-handlers.ts +237 -0
- package/src/tools/handlers/argument-helper.ts +142 -0
- package/src/tools/handlers/asset-handlers.ts +532 -0
- package/src/tools/handlers/audio-handlers.ts +194 -0
- package/src/tools/handlers/blueprint-handlers.ts +380 -0
- package/src/tools/handlers/common-handlers.ts +87 -0
- package/src/tools/handlers/editor-handlers.ts +123 -0
- package/src/tools/handlers/effect-handlers.ts +220 -0
- package/src/tools/handlers/environment-handlers.ts +183 -0
- package/src/tools/handlers/graph-handlers.ts +116 -0
- package/src/tools/handlers/input-handlers.ts +28 -0
- package/src/tools/handlers/inspect-handlers.ts +450 -0
- package/src/tools/handlers/level-handlers.ts +252 -0
- package/src/tools/handlers/lighting-handlers.ts +147 -0
- package/src/tools/handlers/performance-handlers.ts +132 -0
- package/src/tools/handlers/pipeline-handlers.ts +127 -0
- package/src/tools/handlers/sequence-handlers.ts +415 -0
- package/src/tools/handlers/system-handlers.ts +564 -0
- package/src/tools/input.ts +101 -0
- package/src/tools/introspection.ts +493 -584
- package/src/tools/landscape.ts +394 -489
- package/src/tools/level.ts +752 -694
- package/src/tools/lighting.ts +583 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +231 -121
- package/src/tools/niagara.ts +293 -168
- package/src/tools/performance.ts +320 -168
- package/src/tools/physics.ts +268 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +255 -815
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +207 -283
- package/src/types/env.ts +0 -10
- package/src/types/tool-interfaces.ts +250 -0
- package/src/types/tool-types.ts +243 -21
- package/src/unreal-bridge.ts +460 -1550
- package/src/utils/command-validator.ts +75 -0
- package/src/utils/elicitation.ts +10 -7
- package/src/utils/error-handler.ts +14 -90
- package/src/utils/ini-reader.ts +86 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/normalize.ts +60 -0
- package/src/utils/response-factory.ts +39 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.ts +4 -1
- package/src/wasm/index.ts +838 -0
- package/test-server.mjs +100 -0
- package/tests/run-unreal-tool-tests.mjs +242 -14
- package/tests/test-animation.mjs +44 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-audio.mjs +219 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +261 -0
- package/tests/test-blueprint-events.mjs +35 -0
- package/tests/test-blueprint-graph.mjs +79 -0
- package/tests/test-blueprint.mjs +577 -0
- package/tests/test-client-mode.mjs +86 -0
- package/tests/test-console-command.mjs +56 -0
- package/tests/test-control-actor.mjs +425 -0
- package/tests/test-control-editor.mjs +80 -0
- package/tests/test-extra-tools.mjs +38 -0
- package/tests/test-graphql.mjs +322 -0
- package/tests/test-inspect.mjs +72 -0
- package/tests/test-landscape.mjs +60 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +70 -0
- package/tests/test-materials.mjs +356 -0
- package/tests/test-niagara.mjs +185 -0
- package/tests/test-no-inline-python.mjs +122 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-render.mjs +33 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-search-assets.mjs +66 -0
- package/tests/test-sequence.mjs +68 -0
- package/tests/test-system.mjs +57 -0
- package/tests/test-wasm.mjs +193 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/wasm/Cargo.lock +363 -0
- package/wasm/Cargo.toml +42 -0
- package/wasm/LICENSE +21 -0
- package/wasm/README.md +253 -0
- package/wasm/src/dependency_resolver.rs +377 -0
- package/wasm/src/lib.rs +153 -0
- package/wasm/src/property_parser.rs +271 -0
- package/wasm/src/transform_math.rs +396 -0
- package/wasm/tests/integration.rs +109 -0
- package/.github/workflows/smithery-build.yml +0 -29
- package/dist/tools/build_environment_advanced.d.ts +0 -65
- package/dist/tools/build_environment_advanced.js +0 -633
- package/dist/tools/rc.d.ts +0 -110
- package/dist/tools/rc.js +0 -437
- package/dist/tools/visual.d.ts +0 -40
- package/dist/tools/visual.js +0 -282
- package/dist/utils/http.d.ts +0 -6
- package/dist/utils/http.js +0 -151
- package/dist/utils/python-output.d.ts +0 -18
- package/dist/utils/python-output.js +0 -290
- package/dist/utils/python.d.ts +0 -2
- package/dist/utils/python.js +0 -4
- package/dist/utils/stdio-redirect.d.ts +0 -2
- package/dist/utils/stdio-redirect.js +0 -20
- package/docs/unreal-tool-test-cases.md +0 -574
- package/smithery.yaml +0 -29
- package/src/tools/build_environment_advanced.ts +0 -732
- package/src/tools/rc.ts +0 -515
- package/src/tools/visual.ts +0 -281
- package/src/utils/http.ts +0 -187
- package/src/utils/python-output.ts +0 -351
- package/src/utils/python.ts +0 -3
- package/src/utils/stdio-redirect.ts +0 -18
package/dist/tools/foliage.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { coerceBoolean, coerceNumber, coerceString } from '../utils/result-helpers.js';
|
|
2
2
|
export class FoliageTools {
|
|
3
3
|
bridge;
|
|
4
|
-
|
|
4
|
+
automationBridge;
|
|
5
|
+
constructor(bridge, automationBridge) {
|
|
5
6
|
this.bridge = bridge;
|
|
7
|
+
this.automationBridge = automationBridge;
|
|
6
8
|
}
|
|
7
|
-
|
|
8
|
-
// they have proven unreliable and generate engine warnings (failed FindConsoleObject).
|
|
9
|
-
// Instead, we validate inputs and return structured results. Actual foliage
|
|
10
|
-
// authoring should be implemented via Python APIs in future iterations.
|
|
11
|
-
// Add foliage type via Python (creates FoliageType asset properly)
|
|
9
|
+
setAutomationBridge(automationBridge) { this.automationBridge = automationBridge; }
|
|
12
10
|
async addFoliageType(params) {
|
|
13
|
-
// Basic validation to prevent bad inputs like 'undefined' and empty strings
|
|
14
11
|
const errors = [];
|
|
15
12
|
const name = String(params?.name ?? '').trim();
|
|
16
13
|
const meshPath = String(params?.meshPath ?? '').trim();
|
|
@@ -35,194 +32,58 @@ export class FoliageTools {
|
|
|
35
32
|
if (errors.length > 0) {
|
|
36
33
|
return { success: false, error: errors.join('; ') };
|
|
37
34
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
# Try to create factory and set mesh property
|
|
85
|
-
factory = None
|
|
86
|
-
try:
|
|
87
|
-
factory = unreal.FoliageType_InstancedStaticMeshFactory()
|
|
88
|
-
# Try different property names for different UE versions
|
|
89
|
-
try:
|
|
90
|
-
factory.set_editor_property('mesh', mesh)
|
|
91
|
-
except:
|
|
92
|
-
try:
|
|
93
|
-
factory.set_editor_property('static_mesh', mesh)
|
|
94
|
-
except:
|
|
95
|
-
try:
|
|
96
|
-
factory.set_editor_property('source_mesh', mesh)
|
|
97
|
-
except:
|
|
98
|
-
pass # Factory will use default or no mesh
|
|
99
|
-
except:
|
|
100
|
-
res['note'] += '; factory_creation_failed'
|
|
101
|
-
factory = None
|
|
102
|
-
|
|
103
|
-
# Create the asset with or without factory
|
|
104
|
-
if factory:
|
|
105
|
-
asset = asset_tools.create_asset(
|
|
106
|
-
asset_name=name,
|
|
107
|
-
package_path=package_path,
|
|
108
|
-
asset_class=unreal.FoliageType_InstancedStaticMesh,
|
|
109
|
-
factory=factory
|
|
110
|
-
)
|
|
111
|
-
else:
|
|
112
|
-
# Try without factory
|
|
113
|
-
asset = asset_tools.create_asset(
|
|
114
|
-
asset_name=name,
|
|
115
|
-
package_path=package_path,
|
|
116
|
-
asset_class=unreal.FoliageType_InstancedStaticMesh,
|
|
117
|
-
factory=None
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
if asset:
|
|
121
|
-
# Configure foliage properties
|
|
122
|
-
asset.set_editor_property('mesh', mesh)
|
|
123
|
-
if ${params.density !== undefined ? params.density : 1.0} >= 0:
|
|
124
|
-
asset.set_editor_property('density', ${params.density !== undefined ? params.density : 1.0})
|
|
125
|
-
if ${params.randomYaw === false ? 'False' : 'True'}:
|
|
126
|
-
asset.set_editor_property('random_yaw', True)
|
|
127
|
-
if ${params.alignToNormal === false ? 'False' : 'True'}:
|
|
128
|
-
asset.set_editor_property('align_to_normal', True)
|
|
129
|
-
|
|
130
|
-
# Set scale range
|
|
131
|
-
min_scale = ${params.minScale || 0.8}
|
|
132
|
-
max_scale = ${params.maxScale || 1.2}
|
|
133
|
-
asset.set_editor_property('scale_x', (min_scale, max_scale))
|
|
134
|
-
asset.set_editor_property('scale_y', (min_scale, max_scale))
|
|
135
|
-
asset.set_editor_property('scale_z', (min_scale, max_scale))
|
|
136
|
-
|
|
137
|
-
res['note'] += '; created_with_factory'
|
|
138
|
-
else:
|
|
139
|
-
res['note'] += '; factory_creation_failed'
|
|
140
|
-
except AttributeError:
|
|
141
|
-
# Fallback if factory doesn't exist - use base FoliageType
|
|
142
|
-
try:
|
|
143
|
-
asset = asset_tools.create_asset(
|
|
144
|
-
asset_name=name,
|
|
145
|
-
package_path=package_path,
|
|
146
|
-
asset_class=unreal.FoliageType,
|
|
147
|
-
factory=None
|
|
148
|
-
)
|
|
149
|
-
if asset:
|
|
150
|
-
res['note'] += '; created_base_foliage_type'
|
|
151
|
-
except Exception as e2:
|
|
152
|
-
res['note'] += f"; base_creation_failed: {e2}"
|
|
153
|
-
except Exception as e:
|
|
154
|
-
res['note'] += f"; factory_creation_failed: {e}"
|
|
155
|
-
asset = None
|
|
156
|
-
except Exception as e:
|
|
157
|
-
res['note'] += f"; create_asset failed: {e}"
|
|
158
|
-
asset = None
|
|
159
|
-
|
|
160
|
-
if asset and mesh:
|
|
161
|
-
try:
|
|
162
|
-
# Set the mesh property (different property names in different UE versions)
|
|
163
|
-
try:
|
|
164
|
-
asset.set_editor_property('mesh', mesh)
|
|
165
|
-
except:
|
|
166
|
-
try:
|
|
167
|
-
asset.set_editor_property('static_mesh', mesh)
|
|
168
|
-
except:
|
|
169
|
-
pass
|
|
170
|
-
|
|
171
|
-
# Save the asset
|
|
172
|
-
unreal.EditorAssetLibrary.save_asset(asset.get_path_name())
|
|
173
|
-
res['asset_path'] = str(asset.get_path_name())
|
|
174
|
-
res['created'] = True
|
|
175
|
-
res['method'] = 'FoliageType_InstancedStaticMesh'
|
|
176
|
-
except Exception as e:
|
|
177
|
-
res['note'] += f"; set/save asset failed: {e}"
|
|
178
|
-
elif not asset:
|
|
179
|
-
res['note'] += "; asset creation returned None"
|
|
180
|
-
elif not mesh:
|
|
181
|
-
res['note'] += "; mesh object is None, cannot assign to foliage type"
|
|
182
|
-
|
|
183
|
-
# Verify existence
|
|
184
|
-
res['exists_after'] = unreal.EditorAssetLibrary.does_asset_exist(res['asset_path']) if res['asset_path'] else False
|
|
185
|
-
res['success'] = res['exists_after'] or res['created']
|
|
186
|
-
|
|
187
|
-
except Exception as e:
|
|
188
|
-
res['success'] = False
|
|
189
|
-
res['note'] += f"; fatal: {e}"
|
|
190
|
-
|
|
191
|
-
print('RESULT:' + json.dumps(res))
|
|
192
|
-
`.trim();
|
|
193
|
-
const pyResp = await this.bridge.executePython(py);
|
|
194
|
-
const interpreted = interpretStandardResult(pyResp, {
|
|
195
|
-
successMessage: `Foliage type '${name}' processed`,
|
|
196
|
-
failureMessage: 'Add foliage type failed'
|
|
197
|
-
});
|
|
198
|
-
if (!interpreted.success) {
|
|
35
|
+
if (!this.automationBridge) {
|
|
36
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const base = meshPath.includes('.') ? meshPath : `${meshPath}.${meshPath.split('/').filter(Boolean).pop()}`;
|
|
40
|
+
const response = await this.automationBridge.sendAutomationRequest('add_foliage_type', {
|
|
41
|
+
name,
|
|
42
|
+
meshPath: base,
|
|
43
|
+
density: params.density ?? 100,
|
|
44
|
+
radius: params.radius ?? 0,
|
|
45
|
+
minScale: params.minScale ?? 1.0,
|
|
46
|
+
maxScale: params.maxScale ?? 1.0,
|
|
47
|
+
alignToNormal: params.alignToNormal ?? true,
|
|
48
|
+
randomYaw: params.randomYaw ?? true,
|
|
49
|
+
groundSlope: params.groundSlope ?? 45
|
|
50
|
+
}, {
|
|
51
|
+
timeoutMs: 60000
|
|
52
|
+
});
|
|
53
|
+
if (response.success === false) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: response.error || response.message || 'Add foliage type failed',
|
|
57
|
+
note: coerceString(response.result?.note)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const payload = response.result;
|
|
61
|
+
const created = coerceBoolean(payload.created, false) ?? false;
|
|
62
|
+
const exists = coerceBoolean(payload.exists_after, false) ?? created;
|
|
63
|
+
const method = coerceString(payload.method) ?? 'Unknown';
|
|
64
|
+
const assetPath = coerceString(payload.asset_path);
|
|
65
|
+
const usedMesh = coerceString(payload.used_mesh);
|
|
66
|
+
const note = coerceString(payload.note);
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
created,
|
|
70
|
+
exists,
|
|
71
|
+
method,
|
|
72
|
+
assetPath,
|
|
73
|
+
usedMesh,
|
|
74
|
+
note,
|
|
75
|
+
message: exists
|
|
76
|
+
? `Foliage type '${name}' ready (${method})`
|
|
77
|
+
: `Created foliage '${name}' but verification did not find it yet`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
199
81
|
return {
|
|
200
82
|
success: false,
|
|
201
|
-
error:
|
|
202
|
-
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
83
|
+
error: `Failed to add foliage type: ${error instanceof Error ? error.message : String(error)}`
|
|
203
84
|
};
|
|
204
85
|
}
|
|
205
|
-
const payload = interpreted.payload;
|
|
206
|
-
const created = coerceBoolean(payload.created, false) ?? false;
|
|
207
|
-
const exists = coerceBoolean(payload.exists_after, false) ?? created;
|
|
208
|
-
const method = coerceString(payload.method) ?? 'Unknown';
|
|
209
|
-
const assetPath = coerceString(payload.asset_path);
|
|
210
|
-
const usedMesh = coerceString(payload.used_mesh);
|
|
211
|
-
const note = coerceString(payload.note);
|
|
212
|
-
return {
|
|
213
|
-
success: true,
|
|
214
|
-
created,
|
|
215
|
-
exists,
|
|
216
|
-
method,
|
|
217
|
-
assetPath,
|
|
218
|
-
usedMesh,
|
|
219
|
-
note,
|
|
220
|
-
message: exists
|
|
221
|
-
? `Foliage type '${name}' ready (${method})`
|
|
222
|
-
: `Created foliage '${name}' but verification did not find it yet`
|
|
223
|
-
};
|
|
224
86
|
}
|
|
225
|
-
// Paint foliage by placing HISM instances (editor-only)
|
|
226
87
|
async paintFoliage(params) {
|
|
227
88
|
const errors = [];
|
|
228
89
|
const foliageType = String(params?.foliageType ?? '').trim();
|
|
@@ -246,131 +107,90 @@ print('RESULT:' + json.dumps(res))
|
|
|
246
107
|
if (errors.length > 0) {
|
|
247
108
|
return { success: false, error: errors.join('; ') };
|
|
248
109
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if not container:
|
|
282
|
-
raise RuntimeError('Failed to spawn foliage container actor via EditorActorSubsystem')
|
|
283
|
-
try:
|
|
284
|
-
container.set_actor_label(label)
|
|
285
|
-
except Exception:
|
|
286
|
-
pass
|
|
287
|
-
|
|
288
|
-
# Resolve mesh from FoliageType asset
|
|
289
|
-
mesh = None
|
|
290
|
-
fol_asset_path = f"/Game/Foliage/Types/{foliage_type_name}.{foliage_type_name}"
|
|
291
|
-
if unreal.EditorAssetLibrary.does_asset_exist(fol_asset_path):
|
|
292
|
-
try:
|
|
293
|
-
ft_asset = unreal.EditorAssetLibrary.load_asset(fol_asset_path)
|
|
294
|
-
mesh = ft_asset.get_editor_property('mesh')
|
|
295
|
-
except Exception:
|
|
296
|
-
mesh = None
|
|
297
|
-
|
|
298
|
-
if not mesh:
|
|
299
|
-
mesh = unreal.EditorAssetLibrary.load_asset('/Engine/EngineMeshes/Sphere')
|
|
300
|
-
res['note'] += '; used_fallback_mesh'
|
|
301
|
-
|
|
302
|
-
if mesh:
|
|
303
|
-
res['used_mesh'] = str(mesh.get_path_name())
|
|
304
|
-
|
|
305
|
-
# Since HISM components and add_component don't work in this version,
|
|
306
|
-
# spawn individual StaticMeshActors for each instance
|
|
307
|
-
target_count = max(5, int(radius / 20.0))
|
|
308
|
-
added = 0
|
|
309
|
-
for i in range(target_count):
|
|
310
|
-
ang = random.random() * math.tau
|
|
311
|
-
r = random.random() * radius
|
|
312
|
-
x, y, z = px + math.cos(ang) * r, py + math.sin(ang) * r, pz
|
|
313
|
-
try:
|
|
314
|
-
# Spawn static mesh actor at position using modern subsystem
|
|
315
|
-
inst_actor = actor_subsystem.spawn_actor_from_class(
|
|
316
|
-
unreal.StaticMeshActor,
|
|
317
|
-
unreal.Vector(x, y, z),
|
|
318
|
-
unreal.Rotator(0, random.random()*360.0, 0)
|
|
319
|
-
)
|
|
320
|
-
if inst_actor and mesh:
|
|
321
|
-
# Set mesh on the actor's component
|
|
322
|
-
try:
|
|
323
|
-
mesh_comp = inst_actor.static_mesh_component
|
|
324
|
-
if mesh_comp:
|
|
325
|
-
mesh_comp.set_static_mesh(mesh)
|
|
326
|
-
inst_actor.set_actor_label(f"{foliage_type_name}_instance_{i}")
|
|
327
|
-
# Group under the container for organization
|
|
328
|
-
inst_actor.attach_to_actor(container, "", unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False)
|
|
329
|
-
added += 1
|
|
330
|
-
except Exception as e:
|
|
331
|
-
res['note'] += f"; instance_{i} setup failed: {e}"
|
|
332
|
-
except Exception as e:
|
|
333
|
-
res['note'] += f"; spawn instance_{i} failed: {e}"
|
|
334
|
-
|
|
335
|
-
res['added'] = added
|
|
336
|
-
res['actor'] = container.get_actor_label()
|
|
337
|
-
res['component'] = 'StaticMeshActors' # Using actors instead of components
|
|
338
|
-
res['success'] = True
|
|
339
|
-
except Exception as e:
|
|
340
|
-
res['success'] = False
|
|
341
|
-
res['note'] += f"; fatal: {e}"
|
|
342
|
-
|
|
343
|
-
print('RESULT:' + json.dumps(res))
|
|
344
|
-
`.trim();
|
|
345
|
-
const pyResp = await this.bridge.executePython(py);
|
|
346
|
-
const interpreted = interpretStandardResult(pyResp, {
|
|
347
|
-
successMessage: `Painted foliage for '${foliageType}'`,
|
|
348
|
-
failureMessage: 'Paint foliage failed'
|
|
349
|
-
});
|
|
350
|
-
if (!interpreted.success) {
|
|
110
|
+
if (!this.automationBridge) {
|
|
111
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const typePath = foliageType.includes('/') ? foliageType : `/Game/Foliage/${foliageType}.${foliageType}`;
|
|
115
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_foliage', {
|
|
116
|
+
foliageTypePath: typePath,
|
|
117
|
+
locations: [{ x: pos[0], y: pos[1], z: pos[2] }],
|
|
118
|
+
brushSize: Number.isFinite(params.brushSize) ? params.brushSize : 300,
|
|
119
|
+
paintDensity: params.paintDensity,
|
|
120
|
+
eraseMode: params.eraseMode
|
|
121
|
+
}, {
|
|
122
|
+
timeoutMs: 60000
|
|
123
|
+
});
|
|
124
|
+
if (response.success === false) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: response.error || response.message || 'Paint foliage failed',
|
|
128
|
+
note: coerceString(response.result?.note)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const payload = response.result;
|
|
132
|
+
const added = coerceNumber(payload.instancesPlaced) ?? coerceNumber(payload?.count) ?? 0;
|
|
133
|
+
const note = coerceString(payload.note);
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
added,
|
|
137
|
+
note,
|
|
138
|
+
message: `Painted ${added} instances for '${foliageType}' around (${pos[0]}, ${pos[1]}, ${pos[2]})`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
351
142
|
return {
|
|
352
143
|
success: false,
|
|
353
|
-
error:
|
|
354
|
-
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
144
|
+
error: `Failed to paint foliage: ${error instanceof Error ? error.message : String(error)}`
|
|
355
145
|
};
|
|
356
146
|
}
|
|
357
|
-
const payload = interpreted.payload;
|
|
358
|
-
const added = coerceNumber(payload.added) ?? 0;
|
|
359
|
-
const actor = coerceString(payload.actor);
|
|
360
|
-
const component = coerceString(payload.component);
|
|
361
|
-
const usedMesh = coerceString(payload.used_mesh);
|
|
362
|
-
const note = coerceString(payload.note);
|
|
363
|
-
return {
|
|
364
|
-
success: true,
|
|
365
|
-
added,
|
|
366
|
-
actor,
|
|
367
|
-
component,
|
|
368
|
-
usedMesh,
|
|
369
|
-
note,
|
|
370
|
-
message: `Painted ${added} instances for '${foliageType}' around (${pos[0]}, ${pos[1]}, ${pos[2]})`
|
|
371
|
-
};
|
|
372
147
|
}
|
|
373
|
-
|
|
148
|
+
async getFoliageInstances(params) {
|
|
149
|
+
if (!this.automationBridge) {
|
|
150
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const typePath = params.foliageType ? (params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`) : undefined;
|
|
154
|
+
const response = await this.automationBridge.sendAutomationRequest('get_foliage_instances', {
|
|
155
|
+
foliageTypePath: typePath
|
|
156
|
+
}, { timeoutMs: 60000 });
|
|
157
|
+
if (response.success === false) {
|
|
158
|
+
return { success: false, error: response.error || response.message || 'Get foliage instances failed' };
|
|
159
|
+
}
|
|
160
|
+
const payload = response.result;
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
count: coerceNumber(payload.count) ?? 0,
|
|
164
|
+
instances: payload.instances ?? []
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
return { success: false, error: `Failed to get foliage instances: ${error instanceof Error ? error.message : String(error)}` };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async removeFoliage(params) {
|
|
172
|
+
if (!this.automationBridge) {
|
|
173
|
+
throw new Error('Automation Bridge not available. Foliage operations require plugin support.');
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const typePath = params.foliageType ? (params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`) : undefined;
|
|
177
|
+
const response = await this.automationBridge.sendAutomationRequest('remove_foliage', {
|
|
178
|
+
foliageTypePath: typePath,
|
|
179
|
+
removeAll: !!params.removeAll
|
|
180
|
+
}, { timeoutMs: 60000 });
|
|
181
|
+
if (response.success === false) {
|
|
182
|
+
return { success: false, error: response.error || response.message || 'Remove foliage failed' };
|
|
183
|
+
}
|
|
184
|
+
const payload = response.result;
|
|
185
|
+
return {
|
|
186
|
+
success: true,
|
|
187
|
+
instancesRemoved: coerceNumber(payload.instancesRemoved) ?? 0
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return { success: false, error: `Failed to remove foliage: ${error instanceof Error ? error.message : String(error)}` };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
374
194
|
async createInstancedMesh(params) {
|
|
375
195
|
const commands = [];
|
|
376
196
|
commands.push(`CreateInstancedStaticMesh ${params.name} ${params.meshPath}`);
|
|
@@ -388,7 +208,6 @@ print('RESULT:' + json.dumps(res))
|
|
|
388
208
|
await this.bridge.executeConsoleCommands(commands);
|
|
389
209
|
return { success: true, message: `Instanced mesh ${params.name} created with ${params.instances.length} instances` };
|
|
390
210
|
}
|
|
391
|
-
// Set foliage LOD
|
|
392
211
|
async setFoliageLOD(params) {
|
|
393
212
|
const commands = [];
|
|
394
213
|
if (params.lodDistances) {
|
|
@@ -400,24 +219,99 @@ print('RESULT:' + json.dumps(res))
|
|
|
400
219
|
await this.bridge.executeConsoleCommands(commands);
|
|
401
220
|
return { success: true, message: 'Foliage LOD settings updated' };
|
|
402
221
|
}
|
|
403
|
-
|
|
222
|
+
async addFoliage(params) {
|
|
223
|
+
if (params.locations && params.locations.length > 0) {
|
|
224
|
+
if (!this.automationBridge) {
|
|
225
|
+
throw new Error('Automation Bridge not available.');
|
|
226
|
+
}
|
|
227
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_foliage', {
|
|
228
|
+
foliageTypePath: params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`,
|
|
229
|
+
locations: params.locations,
|
|
230
|
+
brushSize: 0,
|
|
231
|
+
paintDensity: 1,
|
|
232
|
+
eraseMode: false
|
|
233
|
+
});
|
|
234
|
+
if (!response.success) {
|
|
235
|
+
return { success: false, error: response.error || 'Failed to add foliage instances' };
|
|
236
|
+
}
|
|
237
|
+
return { success: true, message: `Added ${params.locations.length} foliage instances` };
|
|
238
|
+
}
|
|
239
|
+
return { success: true, message: 'No locations provided for addFoliage' };
|
|
240
|
+
}
|
|
404
241
|
async createProceduralFoliage(params) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
242
|
+
if (!this.automationBridge) {
|
|
243
|
+
throw new Error('Automation Bridge not available.');
|
|
244
|
+
}
|
|
245
|
+
const volName = params.volumeName || params.name || 'ProceduralFoliageVolume';
|
|
246
|
+
const loc = params.bounds?.location ? [params.bounds.location.x, params.bounds.location.y, params.bounds.location.z] : (params.position || [0, 0, 0]);
|
|
247
|
+
const size = params.bounds?.size ? [params.bounds.size.x, params.bounds.size.y, params.bounds.size.z] : (params.size || [1000, 1000, 100]);
|
|
248
|
+
const foliageTypes = Array.isArray(params.foliageTypes)
|
|
249
|
+
? params.foliageTypes.map(t => {
|
|
250
|
+
if (typeof t === 'string')
|
|
251
|
+
return { meshPath: t, density: 0.5 };
|
|
252
|
+
return t;
|
|
253
|
+
})
|
|
254
|
+
: [];
|
|
255
|
+
const payload = {
|
|
256
|
+
name: volName,
|
|
257
|
+
bounds: {
|
|
258
|
+
location: { x: loc[0], y: loc[1], z: loc[2] },
|
|
259
|
+
size: { x: size[0], y: size[1], z: size[2] }
|
|
260
|
+
},
|
|
261
|
+
foliageTypes,
|
|
262
|
+
seed: params.seed ?? 42,
|
|
263
|
+
tileSize: params.tileSize ?? 1000
|
|
264
|
+
};
|
|
265
|
+
const response = await this.automationBridge.sendAutomationRequest('create_procedural_foliage', payload);
|
|
266
|
+
if (!response.success) {
|
|
267
|
+
return {
|
|
268
|
+
success: false,
|
|
269
|
+
error: response.error || 'Failed to create procedural foliage'
|
|
270
|
+
};
|
|
409
271
|
}
|
|
410
|
-
|
|
411
|
-
|
|
272
|
+
const result = response.result;
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
message: `Procedural foliage volume ${volName} created`,
|
|
276
|
+
details: response,
|
|
277
|
+
volumeActor: result?.volume_actor,
|
|
278
|
+
spawnerPath: result?.spawner_path,
|
|
279
|
+
foliageTypesCount: result?.foliage_types_count
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async addFoliageInstances(params) {
|
|
283
|
+
if (!this.automationBridge) {
|
|
284
|
+
throw new Error('Automation Bridge not available. Foliage instance placement requires plugin support.');
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const typePath = params.foliageType.includes('/') ? params.foliageType : `/Game/Foliage/${params.foliageType}.${params.foliageType}`;
|
|
288
|
+
const response = await this.automationBridge.sendAutomationRequest('add_foliage_instances', {
|
|
289
|
+
foliageType: typePath,
|
|
290
|
+
transforms: params.transforms
|
|
291
|
+
}, {
|
|
292
|
+
timeoutMs: 120000
|
|
293
|
+
});
|
|
294
|
+
if (response.success === false) {
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
error: response.error || response.message || 'Failed to add foliage instances',
|
|
298
|
+
message: response.message || 'Failed to add foliage instances'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const result = response.result;
|
|
302
|
+
return {
|
|
303
|
+
success: true,
|
|
304
|
+
message: response.message || `Added ${result?.instances_count || params.transforms.length} foliage instances`,
|
|
305
|
+
instancesCount: result?.instances_count
|
|
306
|
+
};
|
|
412
307
|
}
|
|
413
|
-
|
|
414
|
-
|
|
308
|
+
catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
success: false,
|
|
311
|
+
error: `Failed to add foliage instances: ${error instanceof Error ? error.message : String(error)}`
|
|
312
|
+
};
|
|
415
313
|
}
|
|
416
|
-
commands.push(`GenerateProceduralFoliage ${params.volumeName}`);
|
|
417
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
418
|
-
return { success: true, message: `Procedural foliage volume ${params.volumeName} created` };
|
|
419
314
|
}
|
|
420
|
-
// Set foliage collision
|
|
421
315
|
async setFoliageCollision(params) {
|
|
422
316
|
const commands = [];
|
|
423
317
|
if (params.collisionEnabled !== undefined) {
|
|
@@ -432,7 +326,6 @@ print('RESULT:' + json.dumps(res))
|
|
|
432
326
|
await this.bridge.executeConsoleCommands(commands);
|
|
433
327
|
return { success: true, message: 'Foliage collision settings updated' };
|
|
434
328
|
}
|
|
435
|
-
// Create grass system
|
|
436
329
|
async createGrassSystem(params) {
|
|
437
330
|
const commands = [];
|
|
438
331
|
commands.push(`CreateGrassSystem ${params.name}`);
|
|
@@ -450,12 +343,10 @@ print('RESULT:' + json.dumps(res))
|
|
|
450
343
|
await this.bridge.executeConsoleCommands(commands);
|
|
451
344
|
return { success: true, message: `Grass system ${params.name} created` };
|
|
452
345
|
}
|
|
453
|
-
// Remove foliage instances
|
|
454
346
|
async removeFoliageInstances(params) {
|
|
455
347
|
const command = `RemoveFoliageInRadius ${params.foliageType} ${params.position.join(' ')} ${params.radius}`;
|
|
456
348
|
return this.bridge.executeConsoleCommand(command);
|
|
457
349
|
}
|
|
458
|
-
// Select foliage instances
|
|
459
350
|
async selectFoliageInstances(params) {
|
|
460
351
|
let command;
|
|
461
352
|
if (params.selectAll) {
|
|
@@ -469,7 +360,6 @@ print('RESULT:' + json.dumps(res))
|
|
|
469
360
|
}
|
|
470
361
|
return this.bridge.executeConsoleCommand(command);
|
|
471
362
|
}
|
|
472
|
-
// Update foliage instances
|
|
473
363
|
async updateFoliageInstances(params) {
|
|
474
364
|
const commands = [];
|
|
475
365
|
if (params.updateTransforms) {
|
|
@@ -482,7 +372,6 @@ print('RESULT:' + json.dumps(res))
|
|
|
482
372
|
await this.bridge.executeConsoleCommands(commands);
|
|
483
373
|
return { success: true, message: 'Foliage instances updated' };
|
|
484
374
|
}
|
|
485
|
-
// Create foliage spawner
|
|
486
375
|
async createFoliageSpawner(params) {
|
|
487
376
|
const commands = [];
|
|
488
377
|
commands.push(`CreateFoliageSpawner ${params.name} ${params.spawnArea}`);
|
|
@@ -494,7 +383,6 @@ print('RESULT:' + json.dumps(res))
|
|
|
494
383
|
await this.bridge.executeConsoleCommands(commands);
|
|
495
384
|
return { success: true, message: `Foliage spawner ${params.name} created` };
|
|
496
385
|
}
|
|
497
|
-
// Optimize foliage
|
|
498
386
|
async optimizeFoliage(params) {
|
|
499
387
|
const commands = [];
|
|
500
388
|
if (params.mergeInstances) {
|