unreal-engine-mcp-server 0.4.7 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +26 -0
- package/.env.production +38 -7
- package/.eslintrc.json +0 -54
- package/.eslintrc.override.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- package/.github/copilot-instructions.md +478 -45
- package/.github/dependabot.yml +19 -0
- package/.github/labeler.yml +24 -0
- package/.github/labels.yml +70 -0
- package/.github/pull_request_template.md +42 -0
- package/.github/release-drafter-config.yml +51 -0
- package/.github/workflows/auto-merge.yml +38 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/dependency-review.yml +17 -0
- package/.github/workflows/gemini-issue-triage.yml +172 -0
- package/.github/workflows/greetings.yml +27 -0
- package/.github/workflows/labeler.yml +17 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +13 -7
- package/.github/workflows/release-drafter.yml +23 -0
- package/.github/workflows/release.yml +112 -0
- package/.github/workflows/semantic-pull-request.yml +35 -0
- package/.github/workflows/smoke-test.yml +36 -0
- package/.github/workflows/stale.yml +28 -0
- package/CHANGELOG.md +338 -31
- package/CONTRIBUTING.md +140 -0
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +189 -128
- package/claude_desktop_config_example.json +7 -6
- package/dist/automation/bridge.d.ts +50 -0
- package/dist/automation/bridge.js +452 -0
- package/dist/automation/connection-manager.d.ts +23 -0
- package/dist/automation/connection-manager.js +107 -0
- package/dist/automation/handshake.d.ts +11 -0
- package/dist/automation/handshake.js +89 -0
- package/dist/automation/index.d.ts +3 -0
- package/dist/automation/index.js +3 -0
- package/dist/automation/message-handler.d.ts +12 -0
- package/dist/automation/message-handler.js +149 -0
- package/dist/automation/request-tracker.d.ts +25 -0
- package/dist/automation/request-tracker.js +98 -0
- package/dist/automation/types.d.ts +130 -0
- package/dist/automation/types.js +2 -0
- package/dist/cli.js +32 -5
- package/dist/config.d.ts +26 -0
- package/dist/config.js +59 -0
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +16 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +746 -0
- package/dist/graphql/schema.d.ts +5 -0
- package/dist/graphql/schema.js +437 -0
- package/dist/graphql/server.d.ts +26 -0
- package/dist/graphql/server.js +117 -0
- package/dist/graphql/types.d.ts +9 -0
- package/dist/graphql/types.js +2 -0
- package/dist/handlers/resource-handlers.d.ts +20 -0
- package/dist/handlers/resource-handlers.js +180 -0
- package/dist/index.d.ts +33 -18
- package/dist/index.js +130 -619
- package/dist/resources/actors.d.ts +17 -12
- package/dist/resources/actors.js +56 -76
- package/dist/resources/assets.d.ts +6 -14
- package/dist/resources/assets.js +115 -147
- package/dist/resources/levels.d.ts +13 -13
- package/dist/resources/levels.js +25 -34
- package/dist/server/resource-registry.d.ts +20 -0
- package/dist/server/resource-registry.js +37 -0
- package/dist/server/tool-registry.d.ts +23 -0
- package/dist/server/tool-registry.js +322 -0
- package/dist/server-setup.d.ts +20 -0
- package/dist/server-setup.js +71 -0
- package/dist/services/health-monitor.d.ts +34 -0
- package/dist/services/health-monitor.js +105 -0
- package/dist/services/metrics-server.d.ts +11 -0
- package/dist/services/metrics-server.js +105 -0
- package/dist/tools/actors.d.ts +163 -9
- package/dist/tools/actors.js +356 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +75 -29
- package/dist/tools/assets.js +265 -284
- package/dist/tools/audio.d.ts +102 -42
- package/dist/tools/audio.js +272 -685
- package/dist/tools/base-tool.d.ts +17 -0
- package/dist/tools/base-tool.js +46 -0
- package/dist/tools/behavior-tree.d.ts +94 -0
- package/dist/tools/behavior-tree.js +39 -0
- package/dist/tools/blueprint.d.ts +208 -126
- package/dist/tools/blueprint.js +685 -832
- package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
- package/dist/tools/consolidated-tool-definitions.js +829 -496
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +198 -1027
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +13 -0
- package/dist/tools/dynamic-handler-registry.js +23 -0
- package/dist/tools/editor.d.ts +30 -83
- package/dist/tools/editor.js +247 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +30 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +65 -99
- package/dist/tools/foliage.js +221 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +227 -0
- package/dist/tools/handlers/animation-handlers.d.ts +3 -0
- package/dist/tools/handlers/animation-handlers.js +185 -0
- package/dist/tools/handlers/argument-helper.d.ts +16 -0
- package/dist/tools/handlers/argument-helper.js +80 -0
- package/dist/tools/handlers/asset-handlers.d.ts +3 -0
- package/dist/tools/handlers/asset-handlers.js +496 -0
- package/dist/tools/handlers/audio-handlers.d.ts +3 -0
- package/dist/tools/handlers/audio-handlers.js +166 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
- package/dist/tools/handlers/blueprint-handlers.js +358 -0
- package/dist/tools/handlers/common-handlers.d.ts +14 -0
- package/dist/tools/handlers/common-handlers.js +56 -0
- package/dist/tools/handlers/editor-handlers.d.ts +3 -0
- package/dist/tools/handlers/editor-handlers.js +119 -0
- package/dist/tools/handlers/effect-handlers.d.ts +3 -0
- package/dist/tools/handlers/effect-handlers.js +171 -0
- package/dist/tools/handlers/environment-handlers.d.ts +3 -0
- package/dist/tools/handlers/environment-handlers.js +170 -0
- package/dist/tools/handlers/graph-handlers.d.ts +3 -0
- package/dist/tools/handlers/graph-handlers.js +90 -0
- package/dist/tools/handlers/input-handlers.d.ts +3 -0
- package/dist/tools/handlers/input-handlers.js +21 -0
- package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
- package/dist/tools/handlers/inspect-handlers.js +383 -0
- package/dist/tools/handlers/level-handlers.d.ts +3 -0
- package/dist/tools/handlers/level-handlers.js +237 -0
- package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
- package/dist/tools/handlers/lighting-handlers.js +144 -0
- package/dist/tools/handlers/performance-handlers.d.ts +3 -0
- package/dist/tools/handlers/performance-handlers.js +130 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
- package/dist/tools/handlers/pipeline-handlers.js +110 -0
- package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
- package/dist/tools/handlers/sequence-handlers.js +376 -0
- package/dist/tools/handlers/system-handlers.d.ts +4 -0
- package/dist/tools/handlers/system-handlers.js +506 -0
- package/dist/tools/input.d.ts +19 -0
- package/dist/tools/input.js +89 -0
- package/dist/tools/introspection.d.ts +103 -40
- package/dist/tools/introspection.js +425 -568
- package/dist/tools/landscape.d.ts +54 -93
- package/dist/tools/landscape.js +284 -409
- package/dist/tools/level.d.ts +66 -27
- package/dist/tools/level.js +647 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +445 -943
- package/dist/tools/logs.d.ts +3 -3
- package/dist/tools/logs.js +5 -57
- package/dist/tools/materials.d.ts +91 -24
- package/dist/tools/materials.js +194 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +267 -182
- package/dist/tools/performance.d.ts +27 -13
- package/dist/tools/performance.js +203 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +175 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +85 -60
- package/dist/tools/sequence.js +208 -747
- package/dist/tools/tool-definition-utils.d.ts +59 -0
- package/dist/tools/tool-definition-utils.js +35 -0
- package/dist/tools/ui.d.ts +64 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- package/dist/types/tool-interfaces.d.ts +898 -0
- package/dist/types/tool-interfaces.js +2 -0
- package/dist/types/tool-types.d.ts +183 -19
- package/dist/types/tool-types.js +0 -4
- package/dist/unreal-bridge.d.ts +24 -131
- package/dist/unreal-bridge.js +364 -1506
- package/dist/utils/command-validator.d.ts +9 -0
- package/dist/utils/command-validator.js +68 -0
- package/dist/utils/elicitation.d.ts +1 -1
- package/dist/utils/elicitation.js +12 -15
- package/dist/utils/error-handler.d.ts +2 -51
- package/dist/utils/error-handler.js +11 -87
- package/dist/utils/ini-reader.d.ts +3 -0
- package/dist/utils/ini-reader.js +69 -0
- package/dist/utils/logger.js +9 -6
- package/dist/utils/normalize.d.ts +3 -0
- package/dist/utils/normalize.js +56 -0
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +27 -0
- package/dist/utils/response-validator.d.ts +3 -24
- package/dist/utils/response-validator.js +130 -81
- package/dist/utils/result-helpers.d.ts +4 -5
- package/dist/utils/result-helpers.js +15 -16
- package/dist/utils/safe-json.js +5 -11
- package/dist/utils/unreal-command-queue.d.ts +24 -0
- package/dist/utils/unreal-command-queue.js +120 -0
- package/dist/utils/validation.d.ts +0 -40
- package/dist/utils/validation.js +1 -78
- package/dist/wasm/index.d.ts +70 -0
- package/dist/wasm/index.js +535 -0
- package/docs/GraphQL-API.md +888 -0
- package/docs/Migration-Guide-v0.5.0.md +684 -0
- package/docs/Roadmap.md +53 -0
- package/docs/WebAssembly-Integration.md +628 -0
- package/docs/editor-plugin-extension.md +370 -0
- package/docs/handler-mapping.md +242 -0
- package/docs/native-automation-progress.md +128 -0
- package/docs/testing-guide.md +423 -0
- package/mcp-config-example.json +6 -6
- package/package.json +67 -28
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
- package/scripts/check-unreal-connection.mjs +19 -0
- package/scripts/clean-tmp.js +23 -0
- package/scripts/patch-wasm.js +26 -0
- package/scripts/run-all-tests.mjs +136 -0
- package/scripts/smoke-test.ts +94 -0
- package/scripts/sync-mcp-plugin.js +143 -0
- package/scripts/test-no-plugin-alternates.mjs +113 -0
- package/scripts/validate-server.js +46 -0
- package/scripts/verify-automation-bridge.js +200 -0
- package/server.json +58 -21
- package/src/automation/bridge.ts +558 -0
- package/src/automation/connection-manager.ts +130 -0
- package/src/automation/handshake.ts +99 -0
- package/src/automation/index.ts +2 -0
- package/src/automation/message-handler.ts +167 -0
- package/src/automation/request-tracker.ts +123 -0
- package/src/automation/types.ts +107 -0
- package/src/cli.ts +33 -6
- package/src/config.ts +73 -0
- package/src/constants.ts +19 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +1008 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +156 -0
- package/src/graphql/types.ts +10 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +166 -664
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +148 -134
- package/src/resources/levels.ts +28 -33
- package/src/server/resource-registry.ts +47 -0
- package/src/server/tool-registry.ts +354 -0
- package/src/server-setup.ts +114 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +426 -323
- package/src/tools/animation.ts +672 -461
- package/src/tools/assets.ts +364 -289
- package/src/tools/audio.ts +323 -766
- package/src/tools/base-tool.ts +52 -0
- package/src/tools/behavior-tree.ts +45 -0
- package/src/tools/blueprint.ts +792 -970
- package/src/tools/consolidated-tool-definitions.ts +993 -515
- package/src/tools/consolidated-tool-handlers.ts +258 -1146
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +33 -0
- package/src/tools/editor.ts +329 -253
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +281 -0
- package/src/tools/foliage.ts +330 -392
- package/src/tools/handlers/actor-handlers.ts +265 -0
- package/src/tools/handlers/animation-handlers.ts +237 -0
- package/src/tools/handlers/argument-helper.ts +142 -0
- package/src/tools/handlers/asset-handlers.ts +532 -0
- package/src/tools/handlers/audio-handlers.ts +194 -0
- package/src/tools/handlers/blueprint-handlers.ts +380 -0
- package/src/tools/handlers/common-handlers.ts +87 -0
- package/src/tools/handlers/editor-handlers.ts +123 -0
- package/src/tools/handlers/effect-handlers.ts +220 -0
- package/src/tools/handlers/environment-handlers.ts +183 -0
- package/src/tools/handlers/graph-handlers.ts +116 -0
- package/src/tools/handlers/input-handlers.ts +28 -0
- package/src/tools/handlers/inspect-handlers.ts +450 -0
- package/src/tools/handlers/level-handlers.ts +252 -0
- package/src/tools/handlers/lighting-handlers.ts +147 -0
- package/src/tools/handlers/performance-handlers.ts +132 -0
- package/src/tools/handlers/pipeline-handlers.ts +127 -0
- package/src/tools/handlers/sequence-handlers.ts +415 -0
- package/src/tools/handlers/system-handlers.ts +564 -0
- package/src/tools/input.ts +101 -0
- package/src/tools/introspection.ts +493 -584
- package/src/tools/landscape.ts +418 -507
- package/src/tools/level.ts +786 -708
- package/src/tools/lighting.ts +588 -984
- package/src/tools/logs.ts +9 -57
- package/src/tools/materials.ts +237 -121
- package/src/tools/niagara.ts +335 -168
- package/src/tools/performance.ts +320 -169
- package/src/tools/physics.ts +274 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +276 -820
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +205 -283
- package/src/types/automation-responses.ts +119 -0
- package/src/types/env.ts +0 -10
- package/src/types/responses.ts +355 -0
- package/src/types/tool-interfaces.ts +250 -0
- package/src/types/tool-types.ts +243 -21
- package/src/unreal-bridge.ts +460 -1550
- package/src/utils/command-validator.ts +76 -0
- package/src/utils/elicitation.ts +10 -7
- package/src/utils/error-handler.ts +14 -90
- package/src/utils/ini-reader.ts +86 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/normalize.test.ts +162 -0
- package/src/utils/normalize.ts +60 -0
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +44 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.test.ts +184 -0
- package/src/utils/validation.ts +4 -1
- package/src/wasm/index.ts +838 -0
- package/test-server.mjs +100 -0
- package/tests/run-unreal-tool-tests.mjs +242 -14
- package/tests/test-animation.mjs +369 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +417 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +444 -0
- package/tests/test-blueprint-graph.mjs +410 -0
- package/tests/test-blueprint.mjs +577 -0
- package/tests/test-client-mode.mjs +86 -0
- package/tests/test-console-command.mjs +56 -0
- package/tests/test-control-actor.mjs +425 -0
- package/tests/test-control-editor.mjs +112 -0
- package/tests/test-graphql.mjs +372 -0
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +302 -0
- package/tests/test-landscape.mjs +316 -0
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +89 -0
- package/tests/test-materials.mjs +356 -0
- package/tests/test-niagara.mjs +185 -0
- package/tests/test-no-inline-python.mjs +122 -0
- package/tests/test-performance.mjs +539 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-sequence.mjs +104 -0
- package/tests/test-system.mjs +96 -0
- package/tests/test-wasm.mjs +283 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/vitest.config.ts +35 -0
- package/wasm/Cargo.lock +363 -0
- package/wasm/Cargo.toml +42 -0
- package/wasm/LICENSE +21 -0
- package/wasm/README.md +253 -0
- package/wasm/src/dependency_resolver.rs +377 -0
- package/wasm/src/lib.rs +153 -0
- package/wasm/src/property_parser.rs +271 -0
- package/wasm/src/transform_math.rs +396 -0
- package/wasm/tests/integration.rs +109 -0
- package/.github/workflows/smithery-build.yml +0 -29
- package/dist/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- package/dist/tools/build_environment_advanced.d.ts +0 -65
- package/dist/tools/build_environment_advanced.js +0 -633
- package/dist/tools/rc.d.ts +0 -110
- package/dist/tools/rc.js +0 -437
- package/dist/tools/visual.d.ts +0 -40
- package/dist/tools/visual.js +0 -282
- package/dist/utils/http.d.ts +0 -6
- package/dist/utils/http.js +0 -151
- package/dist/utils/python-output.d.ts +0 -18
- package/dist/utils/python-output.js +0 -290
- package/dist/utils/python.d.ts +0 -2
- package/dist/utils/python.js +0 -4
- package/dist/utils/stdio-redirect.d.ts +0 -2
- package/dist/utils/stdio-redirect.js +0 -20
- package/docs/unreal-tool-test-cases.md +0 -574
- package/smithery.yaml +0 -29
- package/src/prompts/index.ts +0 -249
- package/src/tools/build_environment_advanced.ts +0 -732
- package/src/tools/rc.ts +0 -515
- package/src/tools/visual.ts +0 -281
- package/src/utils/http.ts +0 -187
- package/src/utils/python-output.ts +0 -351
- package/src/utils/python.ts +0 -3
- package/src/utils/stdio-redirect.ts +0 -18
package/dist/tools/landscape.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { bestEffortInterpretedText, coerceBoolean, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
|
|
2
1
|
import { ensureVector3 } from '../utils/validation.js';
|
|
3
|
-
import {
|
|
2
|
+
import { wasmIntegration } from '../wasm/index.js';
|
|
4
3
|
export class LandscapeTools {
|
|
5
4
|
bridge;
|
|
6
|
-
|
|
5
|
+
automationBridge;
|
|
6
|
+
constructor(bridge, automationBridge) {
|
|
7
7
|
this.bridge = bridge;
|
|
8
|
+
this.automationBridge = automationBridge;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
setAutomationBridge(automationBridge) { this.automationBridge = automationBridge; }
|
|
10
11
|
async createLandscape(params) {
|
|
11
12
|
const name = params.name?.trim();
|
|
12
13
|
if (!name) {
|
|
@@ -24,270 +25,53 @@ export class LandscapeTools {
|
|
|
24
25
|
error: 'Landscape sizeY must be a positive number'
|
|
25
26
|
};
|
|
26
27
|
}
|
|
28
|
+
if (!this.automationBridge) {
|
|
29
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
30
|
+
}
|
|
27
31
|
const [locX, locY, locZ] = ensureVector3(params.location ?? [0, 0, 0], 'landscape location');
|
|
32
|
+
const zeroVector = [0, 0, 0];
|
|
33
|
+
const processedLocation = wasmIntegration.vectorAdd(zeroVector, [locX, locY, locZ]);
|
|
34
|
+
console.error('[WASM] Using vectorAdd for landscape positioning');
|
|
28
35
|
const sectionsPerComponent = Math.max(1, Math.floor(params.sectionsPerComponent ?? 1));
|
|
29
36
|
const quadsPerSection = Math.max(1, Math.floor(params.quadsPerSection ?? 63));
|
|
30
|
-
const componentCount = Math.max(1, Math.floor(params.componentCount ?? 1));
|
|
31
|
-
const defaultSize = 1000;
|
|
32
|
-
const scaleX = params.sizeX ? Math.max(0.1, params.sizeX / defaultSize) : 1;
|
|
33
|
-
const scaleY = params.sizeY ? Math.max(0.1, params.sizeY / defaultSize) : 1;
|
|
34
|
-
const escapedName = escapePythonString(name);
|
|
35
|
-
const escapedMaterial = params.materialPath && params.materialPath.trim().length > 0
|
|
36
|
-
? escapePythonString(params.materialPath.trim())
|
|
37
|
-
: '';
|
|
38
|
-
const runtimeGridFlag = params.runtimeGrid ? 'True' : 'False';
|
|
39
|
-
const spatiallyLoadedFlag = params.isSpatiallyLoaded ? 'True' : 'False';
|
|
40
|
-
const runtimeGridValue = params.runtimeGrid ? escapePythonString(params.runtimeGrid.trim()) : '';
|
|
41
|
-
const dataLayerNames = Array.isArray(params.dataLayers)
|
|
42
|
-
? params.dataLayers
|
|
43
|
-
.map(layer => layer?.trim())
|
|
44
|
-
.filter((layer) => Boolean(layer))
|
|
45
|
-
.map(layer => escapePythonString(layer))
|
|
46
|
-
: [];
|
|
47
|
-
const pythonScript = `
|
|
48
|
-
import unreal
|
|
49
|
-
import json
|
|
50
|
-
|
|
51
|
-
result = {
|
|
52
|
-
"success": False,
|
|
53
|
-
"message": "",
|
|
54
|
-
"error": "",
|
|
55
|
-
"warnings": [],
|
|
56
|
-
"details": [],
|
|
57
|
-
"landscapeName": "",
|
|
58
|
-
"landscapeActor": "",
|
|
59
|
-
"worldPartition": False,
|
|
60
|
-
"runtimeGridRequested": ${runtimeGridFlag},
|
|
61
|
-
"spatiallyLoaded": ${spatiallyLoadedFlag}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
|
|
66
|
-
world = editor_subsystem.get_editor_world() if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world') else None
|
|
67
|
-
data_layer_manager = None
|
|
68
|
-
world_partition = None
|
|
69
|
-
if world:
|
|
70
|
-
# Try multiple methods to access World Partition (UE 5.6+)
|
|
71
|
-
try:
|
|
72
|
-
# Method 1: Try get_world_partition() if it exists
|
|
73
|
-
if hasattr(world, 'get_world_partition'):
|
|
74
|
-
world_partition = world.get_world_partition()
|
|
75
|
-
except (AttributeError, Exception):
|
|
76
|
-
pass
|
|
77
|
-
|
|
78
|
-
if not world_partition:
|
|
79
|
-
try:
|
|
80
|
-
# Method 2: Try WorldPartitionSubsystem
|
|
81
|
-
wp_subsystem = unreal.get_editor_subsystem(unreal.WorldPartitionSubsystem)
|
|
82
|
-
if wp_subsystem:
|
|
83
|
-
world_partition = wp_subsystem.get_world_partition(world)
|
|
84
|
-
except (AttributeError, Exception):
|
|
85
|
-
pass
|
|
86
|
-
|
|
87
|
-
if not world_partition:
|
|
88
|
-
try:
|
|
89
|
-
# Method 3: Check if world has world_partition property
|
|
90
|
-
if hasattr(world, 'world_partition'):
|
|
91
|
-
world_partition = world.world_partition
|
|
92
|
-
except (AttributeError, Exception):
|
|
93
|
-
pass
|
|
94
|
-
|
|
95
|
-
result["worldPartition"] = world_partition is not None
|
|
96
|
-
|
|
97
|
-
if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
98
|
-
try:
|
|
99
|
-
data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
|
|
100
|
-
except Exception as dlm_error:
|
|
101
|
-
result["warnings"].append(f"Data layer manager unavailable: {dlm_error}")
|
|
102
|
-
|
|
103
|
-
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
104
|
-
if not actor_subsystem:
|
|
105
|
-
result["error"] = "EditorActorSubsystem unavailable"
|
|
106
|
-
else:
|
|
107
|
-
existing = None
|
|
108
|
-
try:
|
|
109
|
-
for actor in actor_subsystem.get_all_level_actors():
|
|
110
|
-
if actor and actor.get_actor_label() == "${escapedName}":
|
|
111
|
-
existing = actor
|
|
112
|
-
break
|
|
113
|
-
except Exception as scan_error:
|
|
114
|
-
result["warnings"].append(f"Actor scan failed: {scan_error}")
|
|
115
|
-
|
|
116
|
-
if existing:
|
|
117
|
-
result["success"] = True
|
|
118
|
-
result["message"] = "Landscape already exists"
|
|
119
|
-
result["landscapeName"] = existing.get_actor_label()
|
|
120
|
-
try:
|
|
121
|
-
result["landscapeActor"] = existing.get_path_name()
|
|
122
|
-
except Exception:
|
|
123
|
-
pass
|
|
124
|
-
else:
|
|
125
|
-
landscape_class = getattr(unreal, "Landscape", None)
|
|
126
|
-
if not landscape_class:
|
|
127
|
-
result["error"] = "Landscape class unavailable"
|
|
128
|
-
else:
|
|
129
|
-
location = unreal.Vector(${locX}, ${locY}, ${locZ})
|
|
130
|
-
rotation = unreal.Rotator(0.0, 0.0, 0.0)
|
|
131
|
-
landscape_actor = actor_subsystem.spawn_actor_from_class(landscape_class, location, rotation)
|
|
132
|
-
if not landscape_actor:
|
|
133
|
-
result["error"] = "Failed to spawn landscape actor"
|
|
134
|
-
else:
|
|
135
|
-
# Set label first
|
|
136
|
-
try:
|
|
137
|
-
landscape_actor.set_actor_label("${escapedName}", True)
|
|
138
|
-
except TypeError:
|
|
139
|
-
landscape_actor.set_actor_label("${escapedName}")
|
|
140
|
-
except Exception as label_error:
|
|
141
|
-
result["warnings"].append(f"Failed to set landscape label: {label_error}")
|
|
142
|
-
|
|
143
|
-
# Fix component registration by forcing re-registration
|
|
144
|
-
# This addresses the "RegisterComponentWithWorld: Trying to register component with IsValid() == false" warning
|
|
145
|
-
try:
|
|
146
|
-
# Get landscape components and re-register them
|
|
147
|
-
landscape_components = landscape_actor.get_components_by_class(unreal.LandscapeComponent)
|
|
148
|
-
if landscape_components:
|
|
149
|
-
for component in landscape_components:
|
|
150
|
-
if hasattr(component, 'register_component'):
|
|
151
|
-
try:
|
|
152
|
-
component.register_component()
|
|
153
|
-
except Exception:
|
|
154
|
-
pass
|
|
155
|
-
else:
|
|
156
|
-
# If no components yet, this is expected for LandscapePlaceholder
|
|
157
|
-
# The landscape needs to be "finalized" via editor tools or console commands
|
|
158
|
-
result["details"].append("Landscape placeholder created - finalize via editor for full functionality")
|
|
159
|
-
except Exception as comp_error:
|
|
160
|
-
# Component registration is best-effort; not critical
|
|
161
|
-
result["details"].append(f"Component registration attempted (editor finalization may be needed)")
|
|
162
|
-
|
|
163
|
-
try:
|
|
164
|
-
landscape_actor.set_actor_scale3d(unreal.Vector(${scaleX.toFixed(4)}, ${scaleY.toFixed(4)}, 1.0))
|
|
165
|
-
result["details"].append(f"Actor scale set to (${scaleX.toFixed(2)}, ${scaleY.toFixed(2)}, 1.0)")
|
|
166
|
-
except Exception as scale_error:
|
|
167
|
-
result["warnings"].append(f"Failed to set landscape scale: {scale_error}")
|
|
168
|
-
|
|
169
|
-
# Workaround for LandscapeEditorSubsystem Python API limitation
|
|
170
|
-
# Use direct property manipulation instead
|
|
171
|
-
landscape_configured = False
|
|
172
|
-
try:
|
|
173
|
-
# Try LandscapeEditorSubsystem if available (may not be in Python API)
|
|
174
|
-
landscape_editor = unreal.get_editor_subsystem(unreal.LandscapeEditorSubsystem)
|
|
175
|
-
if landscape_editor:
|
|
176
|
-
try:
|
|
177
|
-
landscape_editor.set_component_size(${sectionsPerComponent}, ${quadsPerSection})
|
|
178
|
-
landscape_editor.set_component_count(${componentCount}, ${componentCount})
|
|
179
|
-
result["details"].append(f"Component size ${sectionsPerComponent}x${quadsPerSection}, count ${componentCount}x${componentCount}")
|
|
180
|
-
landscape_configured = True
|
|
181
|
-
except Exception as config_error:
|
|
182
|
-
result["details"].append(f"LandscapeEditorSubsystem method limited: {config_error}")
|
|
183
|
-
except (AttributeError, Exception):
|
|
184
|
-
# Expected - LandscapeEditorSubsystem not available in Python API
|
|
185
|
-
pass
|
|
186
|
-
|
|
187
|
-
# Fallback: Configure via properties if subsystem not available
|
|
188
|
-
if not landscape_configured:
|
|
189
|
-
try:
|
|
190
|
-
# Set component properties directly
|
|
191
|
-
if hasattr(landscape_actor, 'set_editor_property'):
|
|
192
|
-
# Note: These properties may not be directly editable post-spawn
|
|
193
|
-
# This is documented UE limitation - landscape config is best done via editor tools
|
|
194
|
-
result["details"].append(f"Landscape spawned (config via editor tools recommended for ${sectionsPerComponent}x${quadsPerSection} components)")
|
|
195
|
-
except Exception:
|
|
196
|
-
pass
|
|
197
|
-
|
|
198
|
-
${escapedMaterial ? `try:
|
|
199
|
-
material = unreal.EditorAssetLibrary.load_asset("${escapedMaterial}")
|
|
200
|
-
if material:
|
|
201
|
-
try:
|
|
202
|
-
landscape_actor.set_landscape_material(material)
|
|
203
|
-
except Exception:
|
|
204
|
-
landscape_actor.editor_set_landscape_material(material)
|
|
205
|
-
result["details"].append("Landscape material applied")
|
|
206
|
-
else:
|
|
207
|
-
result["warnings"].append("Landscape material asset not found: ${escapedMaterial}")
|
|
208
|
-
except Exception as material_error:
|
|
209
|
-
result["warnings"].append(f"Failed to apply landscape material: {material_error}")
|
|
210
|
-
` : ''}
|
|
211
|
-
${runtimeGridValue ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
212
|
-
try:
|
|
213
|
-
unreal.WorldPartitionBlueprintLibrary.set_actor_runtime_grid(landscape_actor, "${runtimeGridValue}")
|
|
214
|
-
result["details"].append("Runtime grid assigned: ${runtimeGridValue}")
|
|
215
|
-
except Exception as grid_error:
|
|
216
|
-
result["warnings"].append(f"Failed to assign runtime grid: {grid_error}")
|
|
217
|
-
` : ''}
|
|
218
|
-
${params.isSpatiallyLoaded ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
219
|
-
try:
|
|
220
|
-
unreal.WorldPartitionBlueprintLibrary.set_actor_spatially_loaded(landscape_actor, True)
|
|
221
|
-
result["details"].append("Actor marked as spatially loaded")
|
|
222
|
-
except Exception as spatial_error:
|
|
223
|
-
result["warnings"].append(f"Failed to mark as spatially loaded: {spatial_error}")
|
|
224
|
-
` : ''}
|
|
225
|
-
${dataLayerNames.length ? `if result["worldPartition"] and data_layer_manager:
|
|
226
|
-
for layer_name in ${JSON.stringify(dataLayerNames)}:
|
|
227
|
-
try:
|
|
228
|
-
data_layer = data_layer_manager.get_data_layer(layer_name)
|
|
229
|
-
if data_layer:
|
|
230
|
-
unreal.WorldPartitionBlueprintLibrary.add_actor_to_data_layer(landscape_actor, data_layer)
|
|
231
|
-
result["details"].append(f"Added to data layer {layer_name}")
|
|
232
|
-
else:
|
|
233
|
-
result["warnings"].append(f"Data layer not found: {layer_name}")
|
|
234
|
-
except Exception as data_layer_error:
|
|
235
|
-
result["warnings"].append(f"Failed to assign data layer {layer_name}: {data_layer_error}")
|
|
236
|
-
` : ''}
|
|
237
|
-
|
|
238
|
-
try:
|
|
239
|
-
result["landscapeName"] = landscape_actor.get_actor_label()
|
|
240
|
-
result["landscapeActor"] = landscape_actor.get_path_name()
|
|
241
|
-
except Exception:
|
|
242
|
-
pass
|
|
243
|
-
|
|
244
|
-
result["success"] = True
|
|
245
|
-
result["message"] = "Landscape actor created"
|
|
246
|
-
except Exception as e:
|
|
247
|
-
result["error"] = str(e)
|
|
248
|
-
|
|
249
|
-
if result.get("success"):
|
|
250
|
-
result.pop("error", None)
|
|
251
|
-
else:
|
|
252
|
-
if not result.get("error"):
|
|
253
|
-
result["error"] = "Failed to create landscape actor"
|
|
254
|
-
if not result.get("message"):
|
|
255
|
-
result["message"] = result["error"]
|
|
256
|
-
|
|
257
|
-
if not result.get("warnings"):
|
|
258
|
-
result.pop("warnings", None)
|
|
259
|
-
if not result.get("details"):
|
|
260
|
-
result.pop("details", None)
|
|
261
|
-
|
|
262
|
-
print("RESULT:" + json.dumps(result))
|
|
263
|
-
`.trim();
|
|
264
37
|
try {
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
38
|
+
const componentsX = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeX ?? 1000) / 1000)))));
|
|
39
|
+
const componentsY = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeY ?? 1000) / 1000)))));
|
|
40
|
+
const quadsPerComponent = quadsPerSection;
|
|
41
|
+
const payload = {
|
|
42
|
+
name,
|
|
43
|
+
x: processedLocation[0],
|
|
44
|
+
y: processedLocation[1],
|
|
45
|
+
z: processedLocation[2],
|
|
46
|
+
componentsX,
|
|
47
|
+
componentsY,
|
|
48
|
+
quadsPerComponent,
|
|
49
|
+
sectionsPerComponent,
|
|
50
|
+
materialPath: params.materialPath || ''
|
|
51
|
+
};
|
|
52
|
+
const response = await this.automationBridge.sendAutomationRequest('create_landscape', payload, {
|
|
53
|
+
timeoutMs: 60000
|
|
269
54
|
});
|
|
270
|
-
if (
|
|
55
|
+
if (response.success === false) {
|
|
271
56
|
return {
|
|
272
57
|
success: false,
|
|
273
|
-
error:
|
|
58
|
+
error: response.error || response.message || 'Failed to create landscape actor'
|
|
274
59
|
};
|
|
275
60
|
}
|
|
276
61
|
const result = {
|
|
277
62
|
success: true,
|
|
278
|
-
message:
|
|
279
|
-
landscapeName:
|
|
280
|
-
worldPartition:
|
|
63
|
+
message: response.message || 'Landscape actor created',
|
|
64
|
+
landscapeName: response.landscapeName || name,
|
|
65
|
+
worldPartition: response.worldPartition ?? params.enableWorldPartition ?? false
|
|
281
66
|
};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
result.landscapeActor = actorPath;
|
|
67
|
+
if (response.landscapeActor) {
|
|
68
|
+
result.landscapeActor = response.landscapeActor;
|
|
285
69
|
}
|
|
286
|
-
if (
|
|
287
|
-
result.warnings =
|
|
70
|
+
if (response.warnings) {
|
|
71
|
+
result.warnings = response.warnings;
|
|
288
72
|
}
|
|
289
|
-
if (
|
|
290
|
-
result.details =
|
|
73
|
+
if (response.details) {
|
|
74
|
+
result.details = response.details;
|
|
291
75
|
}
|
|
292
76
|
if (params.runtimeGrid) {
|
|
293
77
|
result.runtimeGrid = params.runtimeGrid;
|
|
@@ -300,78 +84,202 @@ print("RESULT:" + json.dumps(result))
|
|
|
300
84
|
catch (error) {
|
|
301
85
|
return {
|
|
302
86
|
success: false,
|
|
303
|
-
error: `Failed to create landscape actor: ${error}`
|
|
87
|
+
error: `Failed to create landscape actor: ${error instanceof Error ? error.message : String(error)}`
|
|
304
88
|
};
|
|
305
89
|
}
|
|
306
90
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
91
|
+
async sculptLandscape(params) {
|
|
92
|
+
const [x, y, z] = ensureVector3(params.location ?? [0, 0, 0], 'sculpt location');
|
|
93
|
+
const tool = (params.tool || '').trim();
|
|
94
|
+
const lowerTool = tool.toLowerCase();
|
|
95
|
+
const validTools = new Set(['sculpt', 'smooth', 'flatten', 'ramp', 'erosion', 'hydro', 'noise', 'raise', 'lower']);
|
|
96
|
+
const isValidTool = lowerTool.length > 0 && validTools.has(lowerTool);
|
|
97
|
+
if (!isValidTool) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: `Invalid sculpt tool: ${params.tool}`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (!this.automationBridge) {
|
|
104
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
105
|
+
}
|
|
106
|
+
const payload = {
|
|
107
|
+
landscapeName: params.landscapeName?.trim(),
|
|
108
|
+
toolMode: tool,
|
|
109
|
+
brushRadius: params.brushSize ?? params.radius ?? 1000,
|
|
110
|
+
brushFalloff: params.brushFalloff ?? 0.5,
|
|
111
|
+
strength: params.strength ?? 0.1,
|
|
112
|
+
location: { x, y, z }
|
|
113
|
+
};
|
|
114
|
+
const response = await this.automationBridge.sendAutomationRequest('sculpt_landscape', payload);
|
|
115
|
+
if (!response.success) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: response.error || 'Failed to sculpt landscape'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
message: `Sculpting applied to ${params.landscapeName}`,
|
|
124
|
+
details: response
|
|
125
|
+
};
|
|
310
126
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
127
|
+
async paintLandscape(params) {
|
|
128
|
+
if (!this.automationBridge) {
|
|
129
|
+
throw new Error('Automation Bridge not available.');
|
|
130
|
+
}
|
|
131
|
+
const [x, y] = ensureVector3(params.position, 'paint position');
|
|
132
|
+
const radius = params.brushSize ?? params.radius ?? 1000;
|
|
133
|
+
const minX = Math.floor(x - radius);
|
|
134
|
+
const maxX = Math.floor(x + radius);
|
|
135
|
+
const minY = Math.floor(y - radius);
|
|
136
|
+
const maxY = Math.floor(y + radius);
|
|
137
|
+
const payload = {
|
|
138
|
+
landscapeName: params.landscapeName?.trim(),
|
|
139
|
+
layerName: params.layerName?.trim(),
|
|
140
|
+
region: { minX, minY, maxX, maxY },
|
|
141
|
+
strength: params.strength ?? 1.0
|
|
142
|
+
};
|
|
143
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_landscape_layer', payload);
|
|
144
|
+
if (!response.success) {
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
error: response.error || 'Failed to paint landscape layer'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
message: `Painted layer ${params.layerName}`,
|
|
153
|
+
details: response
|
|
154
|
+
};
|
|
314
155
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
commands.push(`AddLandscapeLayer ${params.landscapeName} ${params.layerName}`);
|
|
319
|
-
if (params.weightMapPath) {
|
|
320
|
-
commands.push(`SetLayerWeightMap ${params.layerName} ${params.weightMapPath}`);
|
|
156
|
+
async createProceduralTerrain(params) {
|
|
157
|
+
if (!this.automationBridge) {
|
|
158
|
+
throw new Error('Automation Bridge not available. Procedural terrain creation requires plugin support.');
|
|
321
159
|
}
|
|
322
|
-
|
|
323
|
-
|
|
160
|
+
try {
|
|
161
|
+
const payload = {
|
|
162
|
+
name: params.name,
|
|
163
|
+
location: params.location || [0, 0, 0],
|
|
164
|
+
sizeX: params.sizeX || 2000,
|
|
165
|
+
sizeY: params.sizeY || 2000,
|
|
166
|
+
subdivisions: params.subdivisions || 50,
|
|
167
|
+
heightFunction: params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30',
|
|
168
|
+
material: params.material,
|
|
169
|
+
...params.settings
|
|
170
|
+
};
|
|
171
|
+
const response = await this.automationBridge.sendAutomationRequest('create_procedural_terrain', payload, {
|
|
172
|
+
timeoutMs: 120000
|
|
173
|
+
});
|
|
174
|
+
if (response.success === false) {
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
error: response.error || response.message || 'Failed to create procedural terrain',
|
|
178
|
+
message: response.message || 'Failed to create procedural terrain'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const result = response.result;
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
message: response.message || `Created procedural terrain '${params.name}'`,
|
|
185
|
+
actorName: result?.actor_name,
|
|
186
|
+
vertices: result?.vertices,
|
|
187
|
+
triangles: result?.triangles,
|
|
188
|
+
size: result?.size,
|
|
189
|
+
subdivisions: result?.subdivisions,
|
|
190
|
+
details: result
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
success: false,
|
|
196
|
+
error: `Failed to create procedural terrain: ${error instanceof Error ? error.message : String(error)}`
|
|
197
|
+
};
|
|
324
198
|
}
|
|
325
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
326
|
-
return { success: true, message: `Layer ${params.layerName} added to landscape` };
|
|
327
199
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
commands.push(`CreateLandscapeSpline ${params.landscapeName} ${params.splineName}`);
|
|
332
|
-
for (const point of params.points) {
|
|
333
|
-
commands.push(`AddSplinePoint ${params.splineName} ${point.join(' ')}`);
|
|
200
|
+
async createLandscapeGrassType(params) {
|
|
201
|
+
if (!this.automationBridge) {
|
|
202
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
334
203
|
}
|
|
335
|
-
|
|
336
|
-
|
|
204
|
+
const name = typeof params.name === 'string' ? params.name.trim() : '';
|
|
205
|
+
if (!name) {
|
|
206
|
+
return { success: false, error: 'Grass type name is required' };
|
|
337
207
|
}
|
|
338
|
-
|
|
339
|
-
|
|
208
|
+
const meshPathRaw = typeof params.meshPath === 'string' && params.meshPath.trim().length > 0
|
|
209
|
+
? params.meshPath.trim()
|
|
210
|
+
: (typeof params.path === 'string' && params.path.trim().length > 0
|
|
211
|
+
? params.path.trim()
|
|
212
|
+
: (typeof params.staticMesh === 'string' && params.staticMesh.trim().length > 0
|
|
213
|
+
? params.staticMesh.trim()
|
|
214
|
+
: ''));
|
|
215
|
+
if (!meshPathRaw) {
|
|
216
|
+
return { success: false, error: 'meshPath is required to create a landscape grass type' };
|
|
340
217
|
}
|
|
341
|
-
|
|
342
|
-
|
|
218
|
+
try {
|
|
219
|
+
const response = await this.automationBridge.sendAutomationRequest('create_landscape_grass_type', {
|
|
220
|
+
name,
|
|
221
|
+
meshPath: meshPathRaw,
|
|
222
|
+
density: params.density || 1.0,
|
|
223
|
+
minScale: params.minScale || 0.8,
|
|
224
|
+
maxScale: params.maxScale || 1.2
|
|
225
|
+
}, { timeoutMs: 90000 });
|
|
226
|
+
if (response && response.success === false) {
|
|
227
|
+
return {
|
|
228
|
+
success: false,
|
|
229
|
+
error: response.error || response.message || 'Failed to create landscape grass type'
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const result = response.result;
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
message: response?.message || `Landscape grass type '${name}' created`,
|
|
236
|
+
assetPath: result?.asset_path || response?.assetPath || response?.asset_path
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
error: `Failed to create landscape grass type: ${error instanceof Error ? error.message : String(error)}`
|
|
243
|
+
};
|
|
343
244
|
}
|
|
344
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
345
|
-
return { success: true, message: `Landscape spline ${params.splineName} created` };
|
|
346
|
-
}
|
|
347
|
-
// Import heightmap
|
|
348
|
-
async importHeightmap(params) {
|
|
349
|
-
const scale = params.scale || [100, 100, 100];
|
|
350
|
-
const command = `ImportLandscapeHeightmap ${params.landscapeName} ${params.heightmapPath} ${scale.join(' ')}`;
|
|
351
|
-
return this.bridge.executeConsoleCommand(command);
|
|
352
|
-
}
|
|
353
|
-
// Export heightmap
|
|
354
|
-
async exportHeightmap(params) {
|
|
355
|
-
const format = params.format || 'PNG';
|
|
356
|
-
const command = `ExportLandscapeHeightmap ${params.landscapeName} ${params.exportPath} ${format}`;
|
|
357
|
-
return this.bridge.executeConsoleCommand(command);
|
|
358
245
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
363
|
-
|
|
246
|
+
async setLandscapeMaterial(params) {
|
|
247
|
+
const landscapeName = typeof params.landscapeName === 'string' ? params.landscapeName.trim() : '';
|
|
248
|
+
const materialPath = typeof params.materialPath === 'string' ? params.materialPath.trim() : '';
|
|
249
|
+
if (!landscapeName) {
|
|
250
|
+
return { success: false, error: 'Landscape name is required' };
|
|
364
251
|
}
|
|
365
|
-
if (
|
|
366
|
-
|
|
252
|
+
if (!materialPath) {
|
|
253
|
+
return { success: false, error: 'materialPath is required' };
|
|
367
254
|
}
|
|
368
|
-
if (
|
|
369
|
-
|
|
255
|
+
if (!this.automationBridge) {
|
|
256
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const response = await this.automationBridge.sendAutomationRequest('set_landscape_material', {
|
|
260
|
+
landscapeName,
|
|
261
|
+
materialPath
|
|
262
|
+
}, { timeoutMs: 60000 });
|
|
263
|
+
if (response && response.success === false) {
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
error: response.error || response.message || 'Failed to set landscape material'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
message: response?.message || `Landscape material set on '${landscapeName}'`,
|
|
272
|
+
landscapeName: response?.landscapeName || landscapeName,
|
|
273
|
+
materialPath: response?.materialPath || materialPath
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
return {
|
|
278
|
+
success: false,
|
|
279
|
+
error: `Failed to set landscape material: ${error instanceof Error ? error.message : String(error)}`
|
|
280
|
+
};
|
|
370
281
|
}
|
|
371
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
372
|
-
return { success: true, message: 'Landscape LOD settings updated' };
|
|
373
282
|
}
|
|
374
|
-
// Create landscape grass
|
|
375
283
|
async createLandscapeGrass(params) {
|
|
376
284
|
const commands = [];
|
|
377
285
|
commands.push(`CreateLandscapeGrass ${params.landscapeName} ${params.grassType}`);
|
|
@@ -387,7 +295,6 @@ print("RESULT:" + json.dumps(result))
|
|
|
387
295
|
await this.bridge.executeConsoleCommands(commands);
|
|
388
296
|
return { success: true, message: `Grass type ${params.grassType} created on landscape` };
|
|
389
297
|
}
|
|
390
|
-
// Landscape collision
|
|
391
298
|
async updateLandscapeCollision(params) {
|
|
392
299
|
const commands = [];
|
|
393
300
|
if (params.collisionMipLevel !== undefined) {
|
|
@@ -400,7 +307,6 @@ print("RESULT:" + json.dumps(result))
|
|
|
400
307
|
await this.bridge.executeConsoleCommands(commands);
|
|
401
308
|
return { success: true, message: 'Landscape collision updated' };
|
|
402
309
|
}
|
|
403
|
-
// Retopologize landscape
|
|
404
310
|
async retopologizeLandscape(params) {
|
|
405
311
|
const commands = [];
|
|
406
312
|
if (params.targetTriangleCount !== undefined) {
|
|
@@ -413,7 +319,6 @@ print("RESULT:" + json.dumps(result))
|
|
|
413
319
|
await this.bridge.executeConsoleCommands(commands);
|
|
414
320
|
return { success: true, message: 'Landscape retopologized' };
|
|
415
321
|
}
|
|
416
|
-
// Create water body
|
|
417
322
|
async createWaterBody(params) {
|
|
418
323
|
const loc = params.location || [0, 0, 0];
|
|
419
324
|
const size = params.size || [1000, 1000];
|
|
@@ -421,111 +326,39 @@ print("RESULT:" + json.dumps(result))
|
|
|
421
326
|
const command = `CreateWaterBody ${params.type} ${params.name} ${loc.join(' ')} ${size.join(' ')} ${depth}`;
|
|
422
327
|
return this.bridge.executeConsoleCommand(command);
|
|
423
328
|
}
|
|
424
|
-
// World Partition support for landscapes (UE 5.6)
|
|
425
329
|
async configureWorldPartition(params) {
|
|
330
|
+
if (!this.automationBridge) {
|
|
331
|
+
throw new Error('Automation Bridge not available. World Partition operations require plugin support.');
|
|
332
|
+
}
|
|
426
333
|
try {
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
actors = []
|
|
436
|
-
try:
|
|
437
|
-
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
438
|
-
if actor_subsystem and hasattr(actor_subsystem, 'get_all_level_actors'):
|
|
439
|
-
actors = actor_subsystem.get_all_level_actors()
|
|
440
|
-
except Exception:
|
|
441
|
-
actors = []
|
|
442
|
-
landscape = None
|
|
443
|
-
|
|
444
|
-
for actor in actors:
|
|
445
|
-
if actor.get_name() == "${params.landscapeName}" or actor.get_actor_label() == "${params.landscapeName}":
|
|
446
|
-
if isinstance(actor, unreal.LandscapeProxy) or isinstance(actor, unreal.Landscape):
|
|
447
|
-
landscape = actor
|
|
448
|
-
break
|
|
449
|
-
|
|
450
|
-
if landscape:
|
|
451
|
-
changes_made = []
|
|
452
|
-
|
|
453
|
-
# Configure spatial loading (UE 5.6)
|
|
454
|
-
if ${params.enableSpatialLoading !== undefined ? 'True' : 'False'}:
|
|
455
|
-
try:
|
|
456
|
-
landscape.set_editor_property('is_spatially_loaded', ${params.enableSpatialLoading || false})
|
|
457
|
-
changes_made.append("Spatial loading: ${params.enableSpatialLoading}")
|
|
458
|
-
except:
|
|
459
|
-
pass
|
|
460
|
-
|
|
461
|
-
# Set runtime grid (UE 5.6 World Partition)
|
|
462
|
-
if "${params.runtimeGrid || ''}":
|
|
463
|
-
try:
|
|
464
|
-
landscape.set_editor_property('runtime_grid', unreal.Name("${params.runtimeGrid}"))
|
|
465
|
-
changes_made.append("Runtime grid: ${params.runtimeGrid}")
|
|
466
|
-
except:
|
|
467
|
-
pass
|
|
468
|
-
|
|
469
|
-
# Configure data layers (UE 5.6)
|
|
470
|
-
if ${params.dataLayers ? 'True' : 'False'}:
|
|
471
|
-
try:
|
|
472
|
-
# Try modern subsystem first
|
|
473
|
-
try:
|
|
474
|
-
world = None
|
|
475
|
-
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
|
|
476
|
-
if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world'):
|
|
477
|
-
world = editor_subsystem.get_editor_world()
|
|
478
|
-
if world is None:
|
|
479
|
-
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
480
|
-
except Exception:
|
|
481
|
-
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
482
|
-
data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
|
|
483
|
-
if data_layer_manager:
|
|
484
|
-
# Note: Full data layer API requires additional setup
|
|
485
|
-
changes_made.append("Data layers: Requires manual configuration")
|
|
486
|
-
except:
|
|
487
|
-
pass
|
|
488
|
-
|
|
489
|
-
if changes_made:
|
|
490
|
-
result = {
|
|
491
|
-
'success': True,
|
|
492
|
-
'message': 'World Partition configured',
|
|
493
|
-
'changes': changes_made
|
|
494
|
-
}
|
|
495
|
-
else:
|
|
496
|
-
result = {
|
|
497
|
-
'success': False,
|
|
498
|
-
'error': 'No World Partition changes applied'
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
except Exception as e:
|
|
502
|
-
result = {'success': False, 'error': str(e)}
|
|
503
|
-
|
|
504
|
-
print('RESULT:' + json.dumps(result))
|
|
505
|
-
`.trim();
|
|
506
|
-
const response = await this.bridge.executePython(pythonScript);
|
|
507
|
-
const interpreted = interpretStandardResult(response, {
|
|
508
|
-
successMessage: 'World Partition configuration attempted',
|
|
509
|
-
failureMessage: 'World Partition configuration failed'
|
|
334
|
+
const response = await this.automationBridge.sendAutomationRequest('configure_landscape_world_partition', {
|
|
335
|
+
landscapeName: params.landscapeName,
|
|
336
|
+
enableSpatialLoading: params.enableSpatialLoading,
|
|
337
|
+
runtimeGrid: params.runtimeGrid || '',
|
|
338
|
+
dataLayers: params.dataLayers || [],
|
|
339
|
+
streamingDistance: params.streamingDistance
|
|
340
|
+
}, {
|
|
341
|
+
timeoutMs: 60000
|
|
510
342
|
});
|
|
511
|
-
if (
|
|
512
|
-
return
|
|
343
|
+
if (response.success === false) {
|
|
344
|
+
return {
|
|
345
|
+
success: false,
|
|
346
|
+
error: response.error || response.message || 'World Partition configuration failed'
|
|
347
|
+
};
|
|
513
348
|
}
|
|
514
349
|
return {
|
|
515
|
-
success:
|
|
516
|
-
|
|
517
|
-
|
|
350
|
+
success: true,
|
|
351
|
+
message: response.message || 'World Partition configured',
|
|
352
|
+
changes: response.changes
|
|
518
353
|
};
|
|
519
354
|
}
|
|
520
355
|
catch (err) {
|
|
521
|
-
return { success: false, error: `Failed to configure World Partition: ${err}` };
|
|
356
|
+
return { success: false, error: `Failed to configure World Partition: ${err instanceof Error ? err.message : String(err)}` };
|
|
522
357
|
}
|
|
523
358
|
}
|
|
524
|
-
// Set landscape data layers (UE 5.6)
|
|
525
359
|
async setDataLayers(params) {
|
|
526
360
|
try {
|
|
527
361
|
const commands = [];
|
|
528
|
-
// Use console commands for data layer management
|
|
529
362
|
if (params.operation === 'set' || params.operation === 'add') {
|
|
530
363
|
for (const layerName of params.dataLayerNames) {
|
|
531
364
|
commands.push(`wp.Runtime.SetDataLayerRuntimeState Loaded ${layerName}`);
|
|
@@ -536,7 +369,6 @@ print('RESULT:' + json.dumps(result))
|
|
|
536
369
|
commands.push(`wp.Runtime.SetDataLayerRuntimeState Unloaded ${layerName}`);
|
|
537
370
|
}
|
|
538
371
|
}
|
|
539
|
-
// Execute commands
|
|
540
372
|
await this.bridge.executeConsoleCommands(commands);
|
|
541
373
|
return {
|
|
542
374
|
success: true,
|
|
@@ -548,18 +380,15 @@ print('RESULT:' + json.dumps(result))
|
|
|
548
380
|
return { success: false, error: `Failed to manage data layers: ${err}` };
|
|
549
381
|
}
|
|
550
382
|
}
|
|
551
|
-
// Configure landscape streaming cells (UE 5.6 World Partition)
|
|
552
383
|
async configureStreamingCells(params) {
|
|
553
384
|
const commands = [];
|
|
554
|
-
// World Partition runtime commands
|
|
555
385
|
if (params.loadingRange !== undefined) {
|
|
556
386
|
commands.push(`wp.Runtime.OverrideRuntimeSpatialHashLoadingRange -grid=0 -range=${params.loadingRange}`);
|
|
557
387
|
}
|
|
558
388
|
if (params.enableHLOD !== undefined) {
|
|
559
389
|
commands.push(`wp.Runtime.HLOD ${params.enableHLOD ? '1' : '0'}`);
|
|
560
390
|
}
|
|
561
|
-
|
|
562
|
-
commands.push('wp.Runtime.ToggleDrawRuntimeHash2D'); // Show 2D grid
|
|
391
|
+
commands.push('wp.Runtime.ToggleDrawRuntimeHash2D');
|
|
563
392
|
try {
|
|
564
393
|
await this.bridge.executeConsoleCommands(commands);
|
|
565
394
|
return {
|
|
@@ -576,5 +405,51 @@ print('RESULT:' + json.dumps(result))
|
|
|
576
405
|
return { success: false, error: `Failed to configure streaming cells: ${err}` };
|
|
577
406
|
}
|
|
578
407
|
}
|
|
408
|
+
async modifyHeightmap(params) {
|
|
409
|
+
if (!this.automationBridge) {
|
|
410
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
411
|
+
}
|
|
412
|
+
const { landscapeName, heightData, minX, minY, maxX, maxY } = params;
|
|
413
|
+
if (!landscapeName) {
|
|
414
|
+
return { success: false, error: 'Landscape name is required' };
|
|
415
|
+
}
|
|
416
|
+
if (!heightData || !Array.isArray(heightData) || heightData.length === 0) {
|
|
417
|
+
return { success: false, error: 'heightData array is required' };
|
|
418
|
+
}
|
|
419
|
+
const width = maxX - minX + 1;
|
|
420
|
+
const height = maxY - minY + 1;
|
|
421
|
+
if (heightData.length !== width * height) {
|
|
422
|
+
return {
|
|
423
|
+
success: false,
|
|
424
|
+
error: `Height data length (${heightData.length}) does not match region dimensions (${width}x${height} = ${width * height})`
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
const response = await this.automationBridge.sendAutomationRequest('modify_heightmap', {
|
|
429
|
+
landscapeName,
|
|
430
|
+
heightData,
|
|
431
|
+
minX,
|
|
432
|
+
minY,
|
|
433
|
+
maxX,
|
|
434
|
+
maxY,
|
|
435
|
+
updateNormals: params.updateNormals ?? true
|
|
436
|
+
}, {
|
|
437
|
+
timeoutMs: 60000
|
|
438
|
+
});
|
|
439
|
+
if (response.success === false) {
|
|
440
|
+
return {
|
|
441
|
+
success: false,
|
|
442
|
+
error: response.error || response.message || 'Failed to modify heightmap'
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
message: response.message || 'Heightmap modified successfully'
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
return { success: false, error: `Failed to modify heightmap: ${err instanceof Error ? err.message : String(err)}` };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
579
454
|
}
|
|
580
455
|
//# sourceMappingURL=landscape.js.map
|