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/src/tools/landscape.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Landscape tools for Unreal Engine with UE 5.6 World Partition support
|
|
2
2
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
3
|
-
import {
|
|
3
|
+
import { AutomationBridge } from '../automation/index.js';
|
|
4
4
|
import { ensureVector3 } from '../utils/validation.js';
|
|
5
|
-
import {
|
|
5
|
+
import { wasmIntegration } from '../wasm/index.js';
|
|
6
|
+
import { ILandscapeTools, StandardActionResponse } from '../types/tool-interfaces.js';
|
|
6
7
|
|
|
7
|
-
export class LandscapeTools {
|
|
8
|
-
constructor(private bridge: UnrealBridge) {}
|
|
8
|
+
export class LandscapeTools implements ILandscapeTools {
|
|
9
|
+
constructor(private bridge: UnrealBridge, private automationBridge?: AutomationBridge) { }
|
|
10
|
+
|
|
11
|
+
setAutomationBridge(automationBridge?: AutomationBridge) { this.automationBridge = automationBridge; }
|
|
9
12
|
|
|
10
13
|
// Create landscape with World Partition support (UE 5.6)
|
|
11
14
|
async createLandscape(params: {
|
|
@@ -22,7 +25,7 @@ export class LandscapeTools {
|
|
|
22
25
|
runtimeGrid?: string;
|
|
23
26
|
isSpatiallyLoaded?: boolean;
|
|
24
27
|
dataLayers?: string[];
|
|
25
|
-
}) {
|
|
28
|
+
}): Promise<StandardActionResponse> {
|
|
26
29
|
const name = params.name?.trim();
|
|
27
30
|
if (!name) {
|
|
28
31
|
return { success: false, error: 'Landscape name is required' };
|
|
@@ -40,281 +43,63 @@ export class LandscapeTools {
|
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
if (!this.automationBridge) {
|
|
47
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
const [locX, locY, locZ] = ensureVector3(params.location ?? [0, 0, 0], 'landscape location');
|
|
51
|
+
// Use WASM vectorAdd for landscape location processing
|
|
52
|
+
const zeroVector: [number, number, number] = [0, 0, 0];
|
|
53
|
+
const processedLocation = wasmIntegration.vectorAdd(zeroVector, [locX, locY, locZ]);
|
|
54
|
+
console.error('[WASM] Using vectorAdd for landscape positioning');
|
|
44
55
|
const sectionsPerComponent = Math.max(1, Math.floor(params.sectionsPerComponent ?? 1));
|
|
45
56
|
const quadsPerSection = Math.max(1, Math.floor(params.quadsPerSection ?? 63));
|
|
46
|
-
const componentCount = Math.max(1, Math.floor(params.componentCount ?? 1));
|
|
47
|
-
|
|
48
|
-
const defaultSize = 1000;
|
|
49
|
-
const scaleX = params.sizeX ? Math.max(0.1, params.sizeX / defaultSize) : 1;
|
|
50
|
-
const scaleY = params.sizeY ? Math.max(0.1, params.sizeY / defaultSize) : 1;
|
|
51
|
-
|
|
52
|
-
const escapedName = escapePythonString(name);
|
|
53
|
-
const escapedMaterial =
|
|
54
|
-
params.materialPath && params.materialPath.trim().length > 0
|
|
55
|
-
? escapePythonString(params.materialPath.trim())
|
|
56
|
-
: '';
|
|
57
|
-
|
|
58
|
-
const runtimeGridFlag = params.runtimeGrid ? 'True' : 'False';
|
|
59
|
-
const spatiallyLoadedFlag = params.isSpatiallyLoaded ? 'True' : 'False';
|
|
60
|
-
const runtimeGridValue = params.runtimeGrid ? escapePythonString(params.runtimeGrid.trim()) : '';
|
|
61
|
-
const dataLayerNames = Array.isArray(params.dataLayers)
|
|
62
|
-
? params.dataLayers
|
|
63
|
-
.map(layer => layer?.trim())
|
|
64
|
-
.filter((layer): layer is string => Boolean(layer))
|
|
65
|
-
.map(layer => escapePythonString(layer))
|
|
66
|
-
: [];
|
|
67
|
-
|
|
68
|
-
const pythonScript = `
|
|
69
|
-
import unreal
|
|
70
|
-
import json
|
|
71
|
-
|
|
72
|
-
result = {
|
|
73
|
-
"success": False,
|
|
74
|
-
"message": "",
|
|
75
|
-
"error": "",
|
|
76
|
-
"warnings": [],
|
|
77
|
-
"details": [],
|
|
78
|
-
"landscapeName": "",
|
|
79
|
-
"landscapeActor": "",
|
|
80
|
-
"worldPartition": False,
|
|
81
|
-
"runtimeGridRequested": ${runtimeGridFlag},
|
|
82
|
-
"spatiallyLoaded": ${spatiallyLoadedFlag}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try:
|
|
86
|
-
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
|
|
87
|
-
world = editor_subsystem.get_editor_world() if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world') else None
|
|
88
|
-
data_layer_manager = None
|
|
89
|
-
world_partition = None
|
|
90
|
-
if world:
|
|
91
|
-
# Try multiple methods to access World Partition (UE 5.6+)
|
|
92
|
-
try:
|
|
93
|
-
# Method 1: Try get_world_partition() if it exists
|
|
94
|
-
if hasattr(world, 'get_world_partition'):
|
|
95
|
-
world_partition = world.get_world_partition()
|
|
96
|
-
except (AttributeError, Exception):
|
|
97
|
-
pass
|
|
98
|
-
|
|
99
|
-
if not world_partition:
|
|
100
|
-
try:
|
|
101
|
-
# Method 2: Try WorldPartitionSubsystem
|
|
102
|
-
wp_subsystem = unreal.get_editor_subsystem(unreal.WorldPartitionSubsystem)
|
|
103
|
-
if wp_subsystem:
|
|
104
|
-
world_partition = wp_subsystem.get_world_partition(world)
|
|
105
|
-
except (AttributeError, Exception):
|
|
106
|
-
pass
|
|
107
|
-
|
|
108
|
-
if not world_partition:
|
|
109
|
-
try:
|
|
110
|
-
# Method 3: Check if world has world_partition property
|
|
111
|
-
if hasattr(world, 'world_partition'):
|
|
112
|
-
world_partition = world.world_partition
|
|
113
|
-
except (AttributeError, Exception):
|
|
114
|
-
pass
|
|
115
|
-
|
|
116
|
-
result["worldPartition"] = world_partition is not None
|
|
117
|
-
|
|
118
|
-
if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
119
|
-
try:
|
|
120
|
-
data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
|
|
121
|
-
except Exception as dlm_error:
|
|
122
|
-
result["warnings"].append(f"Data layer manager unavailable: {dlm_error}")
|
|
123
|
-
|
|
124
|
-
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
125
|
-
if not actor_subsystem:
|
|
126
|
-
result["error"] = "EditorActorSubsystem unavailable"
|
|
127
|
-
else:
|
|
128
|
-
existing = None
|
|
129
|
-
try:
|
|
130
|
-
for actor in actor_subsystem.get_all_level_actors():
|
|
131
|
-
if actor and actor.get_actor_label() == "${escapedName}":
|
|
132
|
-
existing = actor
|
|
133
|
-
break
|
|
134
|
-
except Exception as scan_error:
|
|
135
|
-
result["warnings"].append(f"Actor scan failed: {scan_error}")
|
|
136
|
-
|
|
137
|
-
if existing:
|
|
138
|
-
result["success"] = True
|
|
139
|
-
result["message"] = "Landscape already exists"
|
|
140
|
-
result["landscapeName"] = existing.get_actor_label()
|
|
141
|
-
try:
|
|
142
|
-
result["landscapeActor"] = existing.get_path_name()
|
|
143
|
-
except Exception:
|
|
144
|
-
pass
|
|
145
|
-
else:
|
|
146
|
-
landscape_class = getattr(unreal, "Landscape", None)
|
|
147
|
-
if not landscape_class:
|
|
148
|
-
result["error"] = "Landscape class unavailable"
|
|
149
|
-
else:
|
|
150
|
-
location = unreal.Vector(${locX}, ${locY}, ${locZ})
|
|
151
|
-
rotation = unreal.Rotator(0.0, 0.0, 0.0)
|
|
152
|
-
landscape_actor = actor_subsystem.spawn_actor_from_class(landscape_class, location, rotation)
|
|
153
|
-
if not landscape_actor:
|
|
154
|
-
result["error"] = "Failed to spawn landscape actor"
|
|
155
|
-
else:
|
|
156
|
-
# Set label first
|
|
157
|
-
try:
|
|
158
|
-
landscape_actor.set_actor_label("${escapedName}", True)
|
|
159
|
-
except TypeError:
|
|
160
|
-
landscape_actor.set_actor_label("${escapedName}")
|
|
161
|
-
except Exception as label_error:
|
|
162
|
-
result["warnings"].append(f"Failed to set landscape label: {label_error}")
|
|
163
|
-
|
|
164
|
-
# Fix component registration by forcing re-registration
|
|
165
|
-
# This addresses the "RegisterComponentWithWorld: Trying to register component with IsValid() == false" warning
|
|
166
|
-
try:
|
|
167
|
-
# Get landscape components and re-register them
|
|
168
|
-
landscape_components = landscape_actor.get_components_by_class(unreal.LandscapeComponent)
|
|
169
|
-
if landscape_components:
|
|
170
|
-
for component in landscape_components:
|
|
171
|
-
if hasattr(component, 'register_component'):
|
|
172
|
-
try:
|
|
173
|
-
component.register_component()
|
|
174
|
-
except Exception:
|
|
175
|
-
pass
|
|
176
|
-
else:
|
|
177
|
-
# If no components yet, this is expected for LandscapePlaceholder
|
|
178
|
-
# The landscape needs to be "finalized" via editor tools or console commands
|
|
179
|
-
result["details"].append("Landscape placeholder created - finalize via editor for full functionality")
|
|
180
|
-
except Exception as comp_error:
|
|
181
|
-
# Component registration is best-effort; not critical
|
|
182
|
-
result["details"].append(f"Component registration attempted (editor finalization may be needed)")
|
|
183
|
-
|
|
184
|
-
try:
|
|
185
|
-
landscape_actor.set_actor_scale3d(unreal.Vector(${scaleX.toFixed(4)}, ${scaleY.toFixed(4)}, 1.0))
|
|
186
|
-
result["details"].append(f"Actor scale set to (${scaleX.toFixed(2)}, ${scaleY.toFixed(2)}, 1.0)")
|
|
187
|
-
except Exception as scale_error:
|
|
188
|
-
result["warnings"].append(f"Failed to set landscape scale: {scale_error}")
|
|
189
|
-
|
|
190
|
-
# Workaround for LandscapeEditorSubsystem Python API limitation
|
|
191
|
-
# Use direct property manipulation instead
|
|
192
|
-
landscape_configured = False
|
|
193
|
-
try:
|
|
194
|
-
# Try LandscapeEditorSubsystem if available (may not be in Python API)
|
|
195
|
-
landscape_editor = unreal.get_editor_subsystem(unreal.LandscapeEditorSubsystem)
|
|
196
|
-
if landscape_editor:
|
|
197
|
-
try:
|
|
198
|
-
landscape_editor.set_component_size(${sectionsPerComponent}, ${quadsPerSection})
|
|
199
|
-
landscape_editor.set_component_count(${componentCount}, ${componentCount})
|
|
200
|
-
result["details"].append(f"Component size ${sectionsPerComponent}x${quadsPerSection}, count ${componentCount}x${componentCount}")
|
|
201
|
-
landscape_configured = True
|
|
202
|
-
except Exception as config_error:
|
|
203
|
-
result["details"].append(f"LandscapeEditorSubsystem method limited: {config_error}")
|
|
204
|
-
except (AttributeError, Exception):
|
|
205
|
-
# Expected - LandscapeEditorSubsystem not available in Python API
|
|
206
|
-
pass
|
|
207
|
-
|
|
208
|
-
# Fallback: Configure via properties if subsystem not available
|
|
209
|
-
if not landscape_configured:
|
|
210
|
-
try:
|
|
211
|
-
# Set component properties directly
|
|
212
|
-
if hasattr(landscape_actor, 'set_editor_property'):
|
|
213
|
-
# Note: These properties may not be directly editable post-spawn
|
|
214
|
-
# This is documented UE limitation - landscape config is best done via editor tools
|
|
215
|
-
result["details"].append(f"Landscape spawned (config via editor tools recommended for ${sectionsPerComponent}x${quadsPerSection} components)")
|
|
216
|
-
except Exception:
|
|
217
|
-
pass
|
|
218
|
-
|
|
219
|
-
${escapedMaterial ? `try:
|
|
220
|
-
material = unreal.EditorAssetLibrary.load_asset("${escapedMaterial}")
|
|
221
|
-
if material:
|
|
222
|
-
try:
|
|
223
|
-
landscape_actor.set_landscape_material(material)
|
|
224
|
-
except Exception:
|
|
225
|
-
landscape_actor.editor_set_landscape_material(material)
|
|
226
|
-
result["details"].append("Landscape material applied")
|
|
227
|
-
else:
|
|
228
|
-
result["warnings"].append("Landscape material asset not found: ${escapedMaterial}")
|
|
229
|
-
except Exception as material_error:
|
|
230
|
-
result["warnings"].append(f"Failed to apply landscape material: {material_error}")
|
|
231
|
-
` : ''}
|
|
232
|
-
${runtimeGridValue ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
233
|
-
try:
|
|
234
|
-
unreal.WorldPartitionBlueprintLibrary.set_actor_runtime_grid(landscape_actor, "${runtimeGridValue}")
|
|
235
|
-
result["details"].append("Runtime grid assigned: ${runtimeGridValue}")
|
|
236
|
-
except Exception as grid_error:
|
|
237
|
-
result["warnings"].append(f"Failed to assign runtime grid: {grid_error}")
|
|
238
|
-
` : ''}
|
|
239
|
-
${params.isSpatiallyLoaded ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
|
|
240
|
-
try:
|
|
241
|
-
unreal.WorldPartitionBlueprintLibrary.set_actor_spatially_loaded(landscape_actor, True)
|
|
242
|
-
result["details"].append("Actor marked as spatially loaded")
|
|
243
|
-
except Exception as spatial_error:
|
|
244
|
-
result["warnings"].append(f"Failed to mark as spatially loaded: {spatial_error}")
|
|
245
|
-
` : ''}
|
|
246
|
-
${dataLayerNames.length ? `if result["worldPartition"] and data_layer_manager:
|
|
247
|
-
for layer_name in ${JSON.stringify(dataLayerNames)}:
|
|
248
|
-
try:
|
|
249
|
-
data_layer = data_layer_manager.get_data_layer(layer_name)
|
|
250
|
-
if data_layer:
|
|
251
|
-
unreal.WorldPartitionBlueprintLibrary.add_actor_to_data_layer(landscape_actor, data_layer)
|
|
252
|
-
result["details"].append(f"Added to data layer {layer_name}")
|
|
253
|
-
else:
|
|
254
|
-
result["warnings"].append(f"Data layer not found: {layer_name}")
|
|
255
|
-
except Exception as data_layer_error:
|
|
256
|
-
result["warnings"].append(f"Failed to assign data layer {layer_name}: {data_layer_error}")
|
|
257
|
-
` : ''}
|
|
258
|
-
|
|
259
|
-
try:
|
|
260
|
-
result["landscapeName"] = landscape_actor.get_actor_label()
|
|
261
|
-
result["landscapeActor"] = landscape_actor.get_path_name()
|
|
262
|
-
except Exception:
|
|
263
|
-
pass
|
|
264
|
-
|
|
265
|
-
result["success"] = True
|
|
266
|
-
result["message"] = "Landscape actor created"
|
|
267
|
-
except Exception as e:
|
|
268
|
-
result["error"] = str(e)
|
|
269
|
-
|
|
270
|
-
if result.get("success"):
|
|
271
|
-
result.pop("error", None)
|
|
272
|
-
else:
|
|
273
|
-
if not result.get("error"):
|
|
274
|
-
result["error"] = "Failed to create landscape actor"
|
|
275
|
-
if not result.get("message"):
|
|
276
|
-
result["message"] = result["error"]
|
|
277
|
-
|
|
278
|
-
if not result.get("warnings"):
|
|
279
|
-
result.pop("warnings", None)
|
|
280
|
-
if not result.get("details"):
|
|
281
|
-
result.pop("details", None)
|
|
282
|
-
|
|
283
|
-
print("RESULT:" + json.dumps(result))
|
|
284
|
-
`.trim();
|
|
285
57
|
|
|
286
58
|
try {
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
59
|
+
// Map to plugin-native payload shape
|
|
60
|
+
const componentsX = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeX ?? 1000) / 1000)))));
|
|
61
|
+
const componentsY = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeY ?? 1000) / 1000)))));
|
|
62
|
+
const quadsPerComponent = quadsPerSection; // Plugin uses quadsPerComponent
|
|
63
|
+
|
|
64
|
+
const payload: Record<string, unknown> = {
|
|
65
|
+
name,
|
|
66
|
+
x: processedLocation[0],
|
|
67
|
+
y: processedLocation[1],
|
|
68
|
+
z: processedLocation[2],
|
|
69
|
+
componentsX,
|
|
70
|
+
componentsY,
|
|
71
|
+
quadsPerComponent,
|
|
72
|
+
sectionsPerComponent,
|
|
73
|
+
materialPath: params.materialPath || ''
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const response = await this.automationBridge.sendAutomationRequest('create_landscape', payload, {
|
|
77
|
+
timeoutMs: 60000
|
|
291
78
|
});
|
|
292
79
|
|
|
293
|
-
if (
|
|
80
|
+
if (response.success === false) {
|
|
294
81
|
return {
|
|
295
82
|
success: false,
|
|
296
|
-
error:
|
|
83
|
+
error: response.error || response.message || 'Failed to create landscape actor'
|
|
297
84
|
};
|
|
298
85
|
}
|
|
299
86
|
|
|
300
87
|
const result: Record<string, unknown> = {
|
|
301
88
|
success: true,
|
|
302
|
-
message:
|
|
303
|
-
landscapeName:
|
|
304
|
-
worldPartition:
|
|
89
|
+
message: response.message || 'Landscape actor created',
|
|
90
|
+
landscapeName: response.landscapeName || name,
|
|
91
|
+
worldPartition: response.worldPartition ?? params.enableWorldPartition ?? false
|
|
305
92
|
};
|
|
306
93
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
result.landscapeActor = actorPath;
|
|
94
|
+
if (response.landscapeActor) {
|
|
95
|
+
result.landscapeActor = response.landscapeActor;
|
|
310
96
|
}
|
|
311
|
-
if (
|
|
312
|
-
result.warnings =
|
|
97
|
+
if (response.warnings) {
|
|
98
|
+
result.warnings = response.warnings;
|
|
313
99
|
}
|
|
314
|
-
if (
|
|
315
|
-
result.details =
|
|
100
|
+
if (response.details) {
|
|
101
|
+
result.details = response.details;
|
|
316
102
|
}
|
|
317
|
-
|
|
318
103
|
if (params.runtimeGrid) {
|
|
319
104
|
result.runtimeGrid = params.runtimeGrid;
|
|
320
105
|
}
|
|
@@ -322,145 +107,278 @@ print("RESULT:" + json.dumps(result))
|
|
|
322
107
|
result.spatiallyLoaded = params.isSpatiallyLoaded;
|
|
323
108
|
}
|
|
324
109
|
|
|
325
|
-
return result;
|
|
110
|
+
return result as StandardActionResponse;
|
|
326
111
|
} catch (error) {
|
|
327
112
|
return {
|
|
328
113
|
success: false,
|
|
329
|
-
error: `Failed to create landscape actor: ${error}`
|
|
114
|
+
error: `Failed to create landscape actor: ${error instanceof Error ? error.message : String(error)}`
|
|
330
115
|
};
|
|
331
116
|
}
|
|
332
117
|
}
|
|
333
118
|
|
|
119
|
+
|
|
334
120
|
// Sculpt landscape
|
|
335
|
-
async sculptLandscape(
|
|
121
|
+
async sculptLandscape(params: {
|
|
336
122
|
landscapeName: string;
|
|
337
|
-
tool:
|
|
123
|
+
tool: string;
|
|
338
124
|
brushSize?: number;
|
|
339
125
|
brushFalloff?: number;
|
|
340
126
|
strength?: number;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
127
|
+
location?: [number, number, number];
|
|
128
|
+
radius?: number;
|
|
129
|
+
}): Promise<StandardActionResponse> {
|
|
130
|
+
const [x, y, z] = ensureVector3(params.location ?? [0, 0, 0], 'sculpt location');
|
|
131
|
+
|
|
132
|
+
const tool = (params.tool || '').trim();
|
|
133
|
+
const lowerTool = tool.toLowerCase();
|
|
134
|
+
const validTools = new Set(['sculpt', 'smooth', 'flatten', 'ramp', 'erosion', 'hydro', 'noise', 'raise', 'lower']);
|
|
135
|
+
const isValidTool = lowerTool.length > 0 && validTools.has(lowerTool);
|
|
136
|
+
|
|
137
|
+
if (!isValidTool) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: `Invalid sculpt tool: ${params.tool}`
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!this.automationBridge) {
|
|
145
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const payload = {
|
|
149
|
+
landscapeName: params.landscapeName?.trim(),
|
|
150
|
+
toolMode: tool, // Map 'tool' to 'toolMode'
|
|
151
|
+
brushRadius: params.brushSize ?? params.radius ?? 1000,
|
|
152
|
+
brushFalloff: params.brushFalloff ?? 0.5,
|
|
153
|
+
strength: params.strength ?? 0.1,
|
|
154
|
+
location: { x, y, z }
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const response = await this.automationBridge.sendAutomationRequest('sculpt_landscape', payload);
|
|
158
|
+
|
|
159
|
+
if (!response.success) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
error: response.error || 'Failed to sculpt landscape'
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
success: true,
|
|
168
|
+
message: `Sculpting applied to ${params.landscapeName}`,
|
|
169
|
+
details: response
|
|
170
|
+
} as StandardActionResponse;
|
|
344
171
|
}
|
|
345
172
|
|
|
346
173
|
// Paint landscape
|
|
347
|
-
async paintLandscape(
|
|
174
|
+
async paintLandscape(params: {
|
|
348
175
|
landscapeName: string;
|
|
349
176
|
layerName: string;
|
|
350
177
|
position: [number, number, number];
|
|
351
178
|
brushSize?: number;
|
|
352
179
|
strength?: number;
|
|
353
180
|
targetValue?: number;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
181
|
+
radius?: number;
|
|
182
|
+
density?: number;
|
|
183
|
+
}): Promise<StandardActionResponse> {
|
|
184
|
+
if (!this.automationBridge) {
|
|
185
|
+
throw new Error('Automation Bridge not available.');
|
|
186
|
+
}
|
|
357
187
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
landscapeName: string;
|
|
361
|
-
layerName: string;
|
|
362
|
-
weightMapPath?: string;
|
|
363
|
-
blendMode?: 'Weight' | 'Alpha';
|
|
364
|
-
}) {
|
|
365
|
-
const commands: string[] = [];
|
|
366
|
-
|
|
367
|
-
commands.push(`AddLandscapeLayer ${params.landscapeName} ${params.layerName}`);
|
|
368
|
-
|
|
369
|
-
if (params.weightMapPath) {
|
|
370
|
-
commands.push(`SetLayerWeightMap ${params.layerName} ${params.weightMapPath}`);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (params.blendMode) {
|
|
374
|
-
commands.push(`SetLayerBlendMode ${params.layerName} ${params.blendMode}`);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
378
|
-
|
|
379
|
-
return { success: true, message: `Layer ${params.layerName} added to landscape` };
|
|
380
|
-
}
|
|
188
|
+
const [x, y] = ensureVector3(params.position, 'paint position');
|
|
189
|
+
const radius = params.brushSize ?? params.radius ?? 1000;
|
|
381
190
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
await this.bridge.executeConsoleCommands(commands);
|
|
412
|
-
|
|
413
|
-
return { success: true, message: `Landscape spline ${params.splineName} created` };
|
|
191
|
+
// Map brush to a square region for now as C++ only supports region fill
|
|
192
|
+
const minX = Math.floor(x - radius);
|
|
193
|
+
const maxX = Math.floor(x + radius);
|
|
194
|
+
const minY = Math.floor(y - radius);
|
|
195
|
+
const maxY = Math.floor(y + radius);
|
|
196
|
+
|
|
197
|
+
const payload = {
|
|
198
|
+
landscapeName: params.landscapeName?.trim(),
|
|
199
|
+
layerName: params.layerName?.trim(),
|
|
200
|
+
region: { minX, minY, maxX, maxY },
|
|
201
|
+
strength: params.strength ?? 1.0
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const response = await this.automationBridge.sendAutomationRequest('paint_landscape_layer', payload);
|
|
205
|
+
|
|
206
|
+
if (!response.success) {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
error: response.error || 'Failed to paint landscape layer'
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
message: `Painted layer ${params.layerName}`,
|
|
216
|
+
details: response
|
|
217
|
+
} as StandardActionResponse;
|
|
414
218
|
}
|
|
415
219
|
|
|
416
|
-
//
|
|
417
|
-
async
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
220
|
+
// Create procedural terrain using ProceduralMeshComponent
|
|
221
|
+
async createProceduralTerrain(params: {
|
|
222
|
+
name: string;
|
|
223
|
+
location?: [number, number, number];
|
|
224
|
+
sizeX?: number;
|
|
225
|
+
sizeY?: number;
|
|
226
|
+
subdivisions?: number;
|
|
227
|
+
heightFunction?: string; // Expression for height calculation
|
|
228
|
+
material?: string;
|
|
229
|
+
settings?: Record<string, unknown>;
|
|
230
|
+
}): Promise<StandardActionResponse> {
|
|
231
|
+
if (!this.automationBridge) {
|
|
232
|
+
throw new Error('Automation Bridge not available. Procedural terrain creation requires plugin support.');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
// Combine specific params with generic settings
|
|
237
|
+
const payload = {
|
|
238
|
+
name: params.name,
|
|
239
|
+
location: params.location || [0, 0, 0],
|
|
240
|
+
sizeX: params.sizeX || 2000,
|
|
241
|
+
sizeY: params.sizeY || 2000,
|
|
242
|
+
subdivisions: params.subdivisions || 50,
|
|
243
|
+
heightFunction: params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30',
|
|
244
|
+
material: params.material,
|
|
245
|
+
...params.settings
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const response = await this.automationBridge.sendAutomationRequest('create_procedural_terrain', payload, {
|
|
249
|
+
timeoutMs: 120000 // 2 minutes for mesh generation
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (response.success === false) {
|
|
253
|
+
return {
|
|
254
|
+
success: false,
|
|
255
|
+
error: response.error || response.message || 'Failed to create procedural terrain',
|
|
256
|
+
message: response.message || 'Failed to create procedural terrain'
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const result = response.result as any;
|
|
261
|
+
return {
|
|
262
|
+
success: true,
|
|
263
|
+
message: response.message || `Created procedural terrain '${params.name}'`,
|
|
264
|
+
actorName: result?.actor_name,
|
|
265
|
+
vertices: result?.vertices,
|
|
266
|
+
triangles: result?.triangles,
|
|
267
|
+
size: result?.size,
|
|
268
|
+
subdivisions: result?.subdivisions,
|
|
269
|
+
details: result
|
|
270
|
+
} as StandardActionResponse;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
error: `Failed to create procedural terrain: ${error instanceof Error ? error.message : String(error)}`
|
|
275
|
+
};
|
|
276
|
+
}
|
|
426
277
|
}
|
|
427
278
|
|
|
428
|
-
//
|
|
429
|
-
async
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
279
|
+
// Create a LandscapeGrassType asset via AutomationBridge
|
|
280
|
+
async createLandscapeGrassType(params: {
|
|
281
|
+
name: string;
|
|
282
|
+
meshPath: string; // Normalized parameter name (was path/staticMesh/meshPath)
|
|
283
|
+
density?: number;
|
|
284
|
+
minScale?: number;
|
|
285
|
+
maxScale?: number;
|
|
286
|
+
path?: string; // Legacy support
|
|
287
|
+
staticMesh?: string; // Legacy support
|
|
288
|
+
}): Promise<StandardActionResponse> {
|
|
289
|
+
if (!this.automationBridge) {
|
|
290
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const name = typeof params.name === 'string' ? params.name.trim() : '';
|
|
294
|
+
if (!name) {
|
|
295
|
+
return { success: false, error: 'Grass type name is required' };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Accept mesh path from multiple fields for compatibility
|
|
299
|
+
const meshPathRaw = typeof params.meshPath === 'string' && params.meshPath.trim().length > 0
|
|
300
|
+
? params.meshPath.trim()
|
|
301
|
+
: (typeof params.path === 'string' && params.path.trim().length > 0
|
|
302
|
+
? params.path.trim()
|
|
303
|
+
: (typeof params.staticMesh === 'string' && params.staticMesh.trim().length > 0
|
|
304
|
+
? params.staticMesh.trim()
|
|
305
|
+
: ''));
|
|
306
|
+
|
|
307
|
+
if (!meshPathRaw) {
|
|
308
|
+
return { success: false, error: 'meshPath is required to create a landscape grass type' };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const response: any = await this.automationBridge.sendAutomationRequest('create_landscape_grass_type', {
|
|
313
|
+
name,
|
|
314
|
+
meshPath: meshPathRaw,
|
|
315
|
+
density: params.density || 1.0,
|
|
316
|
+
minScale: params.minScale || 0.8,
|
|
317
|
+
maxScale: params.maxScale || 1.2
|
|
318
|
+
}, { timeoutMs: 90000 });
|
|
319
|
+
|
|
320
|
+
if (response && response.success === false) {
|
|
321
|
+
return {
|
|
322
|
+
success: false,
|
|
323
|
+
error: response.error || response.message || 'Failed to create landscape grass type'
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const result = response.result as any;
|
|
328
|
+
return {
|
|
329
|
+
success: true,
|
|
330
|
+
message: response?.message || `Landscape grass type '${name}' created`,
|
|
331
|
+
assetPath: result?.asset_path || response?.assetPath || response?.asset_path
|
|
332
|
+
} as StandardActionResponse;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
return {
|
|
335
|
+
success: false,
|
|
336
|
+
error: `Failed to create landscape grass type: ${error instanceof Error ? error.message : String(error)}`
|
|
337
|
+
};
|
|
338
|
+
}
|
|
438
339
|
}
|
|
439
340
|
|
|
440
|
-
// Set landscape
|
|
441
|
-
async
|
|
442
|
-
landscapeName:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
341
|
+
// Set the material used by an existing landscape actor
|
|
342
|
+
async setLandscapeMaterial(params: { landscapeName: string; materialPath: string }): Promise<StandardActionResponse> {
|
|
343
|
+
const landscapeName = typeof params.landscapeName === 'string' ? params.landscapeName.trim() : '';
|
|
344
|
+
const materialPath = typeof params.materialPath === 'string' ? params.materialPath.trim() : '';
|
|
345
|
+
|
|
346
|
+
if (!landscapeName) {
|
|
347
|
+
return { success: false, error: 'Landscape name is required' };
|
|
348
|
+
}
|
|
349
|
+
if (!materialPath) {
|
|
350
|
+
return { success: false, error: 'materialPath is required' };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!this.automationBridge) {
|
|
354
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
const response: any = await this.automationBridge.sendAutomationRequest('set_landscape_material', {
|
|
359
|
+
landscapeName,
|
|
360
|
+
materialPath
|
|
361
|
+
}, { timeoutMs: 60000 });
|
|
362
|
+
|
|
363
|
+
if (response && response.success === false) {
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
error: response.error || response.message || 'Failed to set landscape material'
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
success: true,
|
|
372
|
+
message: response?.message || `Landscape material set on '${landscapeName}'`,
|
|
373
|
+
landscapeName: response?.landscapeName || landscapeName,
|
|
374
|
+
materialPath: response?.materialPath || materialPath
|
|
375
|
+
} as StandardActionResponse;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
error: `Failed to set landscape material: ${error instanceof Error ? error.message : String(error)}`
|
|
380
|
+
};
|
|
381
|
+
}
|
|
464
382
|
}
|
|
465
383
|
|
|
466
384
|
// Create landscape grass
|
|
@@ -471,25 +389,25 @@ print("RESULT:" + json.dumps(result))
|
|
|
471
389
|
minScale?: number;
|
|
472
390
|
maxScale?: number;
|
|
473
391
|
randomRotation?: boolean;
|
|
474
|
-
}) {
|
|
475
|
-
|
|
476
|
-
|
|
392
|
+
}): Promise<StandardActionResponse> {
|
|
393
|
+
const commands: string[] = [];
|
|
394
|
+
|
|
477
395
|
commands.push(`CreateLandscapeGrass ${params.landscapeName} ${params.grassType}`);
|
|
478
|
-
|
|
396
|
+
|
|
479
397
|
if (params.density !== undefined) {
|
|
480
398
|
commands.push(`SetGrassDensity ${params.grassType} ${params.density}`);
|
|
481
399
|
}
|
|
482
|
-
|
|
400
|
+
|
|
483
401
|
if (params.minScale !== undefined && params.maxScale !== undefined) {
|
|
484
402
|
commands.push(`SetGrassScale ${params.grassType} ${params.minScale} ${params.maxScale}`);
|
|
485
403
|
}
|
|
486
|
-
|
|
404
|
+
|
|
487
405
|
if (params.randomRotation !== undefined) {
|
|
488
406
|
commands.push(`SetGrassRandomRotation ${params.grassType} ${params.randomRotation}`);
|
|
489
407
|
}
|
|
490
|
-
|
|
408
|
+
|
|
491
409
|
await this.bridge.executeConsoleCommands(commands);
|
|
492
|
-
|
|
410
|
+
|
|
493
411
|
return { success: true, message: `Grass type ${params.grassType} created on landscape` };
|
|
494
412
|
}
|
|
495
413
|
|
|
@@ -498,21 +416,21 @@ print("RESULT:" + json.dumps(result))
|
|
|
498
416
|
landscapeName: string;
|
|
499
417
|
collisionMipLevel?: number;
|
|
500
418
|
simpleCollision?: boolean;
|
|
501
|
-
}) {
|
|
502
|
-
|
|
503
|
-
|
|
419
|
+
}): Promise<StandardActionResponse> {
|
|
420
|
+
const commands: string[] = [];
|
|
421
|
+
|
|
504
422
|
if (params.collisionMipLevel !== undefined) {
|
|
505
423
|
commands.push(`SetLandscapeCollisionMipLevel ${params.landscapeName} ${params.collisionMipLevel}`);
|
|
506
424
|
}
|
|
507
|
-
|
|
425
|
+
|
|
508
426
|
if (params.simpleCollision !== undefined) {
|
|
509
427
|
commands.push(`SetLandscapeSimpleCollision ${params.landscapeName} ${params.simpleCollision}`);
|
|
510
428
|
}
|
|
511
|
-
|
|
429
|
+
|
|
512
430
|
commands.push(`UpdateLandscapeCollision ${params.landscapeName}`);
|
|
513
|
-
|
|
431
|
+
|
|
514
432
|
await this.bridge.executeConsoleCommands(commands);
|
|
515
|
-
|
|
433
|
+
|
|
516
434
|
return { success: true, message: 'Landscape collision updated' };
|
|
517
435
|
}
|
|
518
436
|
|
|
@@ -521,21 +439,21 @@ print("RESULT:" + json.dumps(result))
|
|
|
521
439
|
landscapeName: string;
|
|
522
440
|
targetTriangleCount?: number;
|
|
523
441
|
preserveDetails?: boolean;
|
|
524
|
-
}) {
|
|
525
|
-
|
|
526
|
-
|
|
442
|
+
}): Promise<StandardActionResponse> {
|
|
443
|
+
const commands: string[] = [];
|
|
444
|
+
|
|
527
445
|
if (params.targetTriangleCount !== undefined) {
|
|
528
446
|
commands.push(`SetRetopologizeTarget ${params.targetTriangleCount}`);
|
|
529
447
|
}
|
|
530
|
-
|
|
448
|
+
|
|
531
449
|
if (params.preserveDetails !== undefined) {
|
|
532
450
|
commands.push(`SetRetopologizePreserveDetails ${params.preserveDetails}`);
|
|
533
451
|
}
|
|
534
|
-
|
|
452
|
+
|
|
535
453
|
commands.push(`RetopologizeLandscape ${params.landscapeName}`);
|
|
536
|
-
|
|
454
|
+
|
|
537
455
|
await this.bridge.executeConsoleCommands(commands);
|
|
538
|
-
|
|
456
|
+
|
|
539
457
|
return { success: true, message: 'Landscape retopologized' };
|
|
540
458
|
}
|
|
541
459
|
|
|
@@ -546,13 +464,13 @@ print("RESULT:" + json.dumps(result))
|
|
|
546
464
|
location?: [number, number, number];
|
|
547
465
|
size?: [number, number];
|
|
548
466
|
depth?: number;
|
|
549
|
-
}) {
|
|
467
|
+
}): Promise<StandardActionResponse> {
|
|
550
468
|
const loc = params.location || [0, 0, 0];
|
|
551
469
|
const size = params.size || [1000, 1000];
|
|
552
470
|
const depth = params.depth || 100;
|
|
553
|
-
|
|
471
|
+
|
|
554
472
|
const command = `CreateWaterBody ${params.type} ${params.name} ${loc.join(' ')} ${size.join(' ')} ${depth}`;
|
|
555
|
-
|
|
473
|
+
|
|
556
474
|
return this.bridge.executeConsoleCommand(command);
|
|
557
475
|
}
|
|
558
476
|
|
|
@@ -563,105 +481,36 @@ print("RESULT:" + json.dumps(result))
|
|
|
563
481
|
runtimeGrid?: string;
|
|
564
482
|
dataLayers?: string[];
|
|
565
483
|
streamingDistance?: number;
|
|
566
|
-
}) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
import json
|
|
571
|
-
|
|
572
|
-
result = {'success': False, 'error': 'Landscape not found'}
|
|
573
|
-
|
|
574
|
-
try:
|
|
575
|
-
# Get the landscape actor using modern EditorActorSubsystem
|
|
576
|
-
actors = []
|
|
577
|
-
try:
|
|
578
|
-
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
579
|
-
if actor_subsystem and hasattr(actor_subsystem, 'get_all_level_actors'):
|
|
580
|
-
actors = actor_subsystem.get_all_level_actors()
|
|
581
|
-
except Exception:
|
|
582
|
-
actors = []
|
|
583
|
-
landscape = None
|
|
584
|
-
|
|
585
|
-
for actor in actors:
|
|
586
|
-
if actor.get_name() == "${params.landscapeName}" or actor.get_actor_label() == "${params.landscapeName}":
|
|
587
|
-
if isinstance(actor, unreal.LandscapeProxy) or isinstance(actor, unreal.Landscape):
|
|
588
|
-
landscape = actor
|
|
589
|
-
break
|
|
590
|
-
|
|
591
|
-
if landscape:
|
|
592
|
-
changes_made = []
|
|
593
|
-
|
|
594
|
-
# Configure spatial loading (UE 5.6)
|
|
595
|
-
if ${params.enableSpatialLoading !== undefined ? 'True' : 'False'}:
|
|
596
|
-
try:
|
|
597
|
-
landscape.set_editor_property('is_spatially_loaded', ${params.enableSpatialLoading || false})
|
|
598
|
-
changes_made.append("Spatial loading: ${params.enableSpatialLoading}")
|
|
599
|
-
except:
|
|
600
|
-
pass
|
|
601
|
-
|
|
602
|
-
# Set runtime grid (UE 5.6 World Partition)
|
|
603
|
-
if "${params.runtimeGrid || ''}":
|
|
604
|
-
try:
|
|
605
|
-
landscape.set_editor_property('runtime_grid', unreal.Name("${params.runtimeGrid}"))
|
|
606
|
-
changes_made.append("Runtime grid: ${params.runtimeGrid}")
|
|
607
|
-
except:
|
|
608
|
-
pass
|
|
609
|
-
|
|
610
|
-
# Configure data layers (UE 5.6)
|
|
611
|
-
if ${params.dataLayers ? 'True' : 'False'}:
|
|
612
|
-
try:
|
|
613
|
-
# Try modern subsystem first
|
|
614
|
-
try:
|
|
615
|
-
world = None
|
|
616
|
-
editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
|
|
617
|
-
if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world'):
|
|
618
|
-
world = editor_subsystem.get_editor_world()
|
|
619
|
-
if world is None:
|
|
620
|
-
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
621
|
-
except Exception:
|
|
622
|
-
world = unreal.EditorSubsystemLibrary.get_editor_world()
|
|
623
|
-
data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
|
|
624
|
-
if data_layer_manager:
|
|
625
|
-
# Note: Full data layer API requires additional setup
|
|
626
|
-
changes_made.append("Data layers: Requires manual configuration")
|
|
627
|
-
except:
|
|
628
|
-
pass
|
|
629
|
-
|
|
630
|
-
if changes_made:
|
|
631
|
-
result = {
|
|
632
|
-
'success': True,
|
|
633
|
-
'message': 'World Partition configured',
|
|
634
|
-
'changes': changes_made
|
|
635
|
-
}
|
|
636
|
-
else:
|
|
637
|
-
result = {
|
|
638
|
-
'success': False,
|
|
639
|
-
'error': 'No World Partition changes applied'
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
except Exception as e:
|
|
643
|
-
result = {'success': False, 'error': str(e)}
|
|
644
|
-
|
|
645
|
-
print('RESULT:' + json.dumps(result))
|
|
646
|
-
`.trim();
|
|
484
|
+
}): Promise<StandardActionResponse> {
|
|
485
|
+
if (!this.automationBridge) {
|
|
486
|
+
throw new Error('Automation Bridge not available. World Partition operations require plugin support.');
|
|
487
|
+
}
|
|
647
488
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
489
|
+
try {
|
|
490
|
+
const response = await this.automationBridge.sendAutomationRequest('configure_landscape_world_partition', {
|
|
491
|
+
landscapeName: params.landscapeName,
|
|
492
|
+
enableSpatialLoading: params.enableSpatialLoading,
|
|
493
|
+
runtimeGrid: params.runtimeGrid || '',
|
|
494
|
+
dataLayers: params.dataLayers || [],
|
|
495
|
+
streamingDistance: params.streamingDistance
|
|
496
|
+
}, {
|
|
497
|
+
timeoutMs: 60000
|
|
498
|
+
});
|
|
653
499
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
500
|
+
if (response.success === false) {
|
|
501
|
+
return {
|
|
502
|
+
success: false,
|
|
503
|
+
error: response.error || response.message || 'World Partition configuration failed'
|
|
504
|
+
};
|
|
505
|
+
}
|
|
657
506
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
507
|
+
return {
|
|
508
|
+
success: true,
|
|
509
|
+
message: response.message || 'World Partition configured',
|
|
510
|
+
changes: response.changes
|
|
511
|
+
} as StandardActionResponse;
|
|
663
512
|
} catch (err) {
|
|
664
|
-
return { success: false, error: `Failed to configure World Partition: ${err}` };
|
|
513
|
+
return { success: false, error: `Failed to configure World Partition: ${err instanceof Error ? err.message : String(err)}` };
|
|
665
514
|
}
|
|
666
515
|
}
|
|
667
516
|
|
|
@@ -670,10 +519,10 @@ print('RESULT:' + json.dumps(result))
|
|
|
670
519
|
landscapeName: string;
|
|
671
520
|
dataLayerNames: string[];
|
|
672
521
|
operation: 'add' | 'remove' | 'set';
|
|
673
|
-
}) {
|
|
522
|
+
}): Promise<StandardActionResponse> {
|
|
674
523
|
try {
|
|
675
524
|
const commands = [];
|
|
676
|
-
|
|
525
|
+
|
|
677
526
|
// Use console commands for data layer management
|
|
678
527
|
if (params.operation === 'set' || params.operation === 'add') {
|
|
679
528
|
for (const layerName of params.dataLayerNames) {
|
|
@@ -684,15 +533,15 @@ print('RESULT:' + json.dumps(result))
|
|
|
684
533
|
commands.push(`wp.Runtime.SetDataLayerRuntimeState Unloaded ${layerName}`);
|
|
685
534
|
}
|
|
686
535
|
}
|
|
687
|
-
|
|
536
|
+
|
|
688
537
|
// Execute commands
|
|
689
538
|
await this.bridge.executeConsoleCommands(commands);
|
|
690
|
-
|
|
691
|
-
return {
|
|
692
|
-
success: true,
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
success: true,
|
|
693
542
|
message: `Data layers ${params.operation === 'add' ? 'added' : params.operation === 'remove' ? 'removed' : 'set'} for landscape`,
|
|
694
543
|
layers: params.dataLayerNames
|
|
695
|
-
};
|
|
544
|
+
} as StandardActionResponse;
|
|
696
545
|
} catch (err) {
|
|
697
546
|
return { success: false, error: `Failed to manage data layers: ${err}` };
|
|
698
547
|
}
|
|
@@ -704,35 +553,97 @@ print('RESULT:' + json.dumps(result))
|
|
|
704
553
|
cellSize?: number;
|
|
705
554
|
loadingRange?: number;
|
|
706
555
|
enableHLOD?: boolean;
|
|
707
|
-
}) {
|
|
556
|
+
}): Promise<StandardActionResponse> {
|
|
708
557
|
const commands = [];
|
|
709
|
-
|
|
558
|
+
|
|
710
559
|
// World Partition runtime commands
|
|
711
560
|
if (params.loadingRange !== undefined) {
|
|
712
561
|
commands.push(`wp.Runtime.OverrideRuntimeSpatialHashLoadingRange -grid=0 -range=${params.loadingRange}`);
|
|
713
562
|
}
|
|
714
|
-
|
|
563
|
+
|
|
715
564
|
if (params.enableHLOD !== undefined) {
|
|
716
565
|
commands.push(`wp.Runtime.HLOD ${params.enableHLOD ? '1' : '0'}`);
|
|
717
566
|
}
|
|
718
|
-
|
|
567
|
+
|
|
719
568
|
// Debug visualization commands
|
|
720
569
|
commands.push('wp.Runtime.ToggleDrawRuntimeHash2D'); // Show 2D grid
|
|
721
|
-
|
|
570
|
+
|
|
722
571
|
try {
|
|
723
572
|
await this.bridge.executeConsoleCommands(commands);
|
|
724
|
-
|
|
725
|
-
return {
|
|
726
|
-
success: true,
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
success: true,
|
|
727
576
|
message: 'Streaming cells configured for World Partition',
|
|
728
577
|
settings: {
|
|
729
578
|
cellSize: params.cellSize,
|
|
730
579
|
loadingRange: params.loadingRange,
|
|
731
580
|
hlod: params.enableHLOD
|
|
732
581
|
}
|
|
733
|
-
};
|
|
582
|
+
} as StandardActionResponse;
|
|
734
583
|
} catch (err) {
|
|
735
584
|
return { success: false, error: `Failed to configure streaming cells: ${err}` };
|
|
736
585
|
}
|
|
737
586
|
}
|
|
587
|
+
|
|
588
|
+
// Modify landscape heightmap
|
|
589
|
+
async modifyHeightmap(params: {
|
|
590
|
+
landscapeName: string;
|
|
591
|
+
heightData: number[];
|
|
592
|
+
minX: number;
|
|
593
|
+
minY: number;
|
|
594
|
+
maxX: number;
|
|
595
|
+
maxY: number;
|
|
596
|
+
updateNormals?: boolean;
|
|
597
|
+
}): Promise<StandardActionResponse> {
|
|
598
|
+
if (!this.automationBridge) {
|
|
599
|
+
throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const { landscapeName, heightData, minX, minY, maxX, maxY } = params;
|
|
603
|
+
|
|
604
|
+
if (!landscapeName) {
|
|
605
|
+
return { success: false, error: 'Landscape name is required' };
|
|
606
|
+
}
|
|
607
|
+
if (!heightData || !Array.isArray(heightData) || heightData.length === 0) {
|
|
608
|
+
return { success: false, error: 'heightData array is required' };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const width = maxX - minX + 1;
|
|
612
|
+
const height = maxY - minY + 1;
|
|
613
|
+
if (heightData.length !== width * height) {
|
|
614
|
+
return {
|
|
615
|
+
success: false,
|
|
616
|
+
error: `Height data length (${heightData.length}) does not match region dimensions (${width}x${height} = ${width * height})`
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
try {
|
|
621
|
+
const response = await this.automationBridge.sendAutomationRequest('modify_heightmap', {
|
|
622
|
+
landscapeName,
|
|
623
|
+
heightData,
|
|
624
|
+
minX,
|
|
625
|
+
minY,
|
|
626
|
+
maxX,
|
|
627
|
+
maxY,
|
|
628
|
+
updateNormals: params.updateNormals ?? true
|
|
629
|
+
}, {
|
|
630
|
+
timeoutMs: 60000
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
if (response.success === false) {
|
|
634
|
+
return {
|
|
635
|
+
success: false,
|
|
636
|
+
error: response.error || response.message || 'Failed to modify heightmap'
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return {
|
|
641
|
+
success: true,
|
|
642
|
+
message: response.message || 'Heightmap modified successfully'
|
|
643
|
+
} as StandardActionResponse;
|
|
644
|
+
} catch (err) {
|
|
645
|
+
return { success: false, error: `Failed to modify heightmap: ${err instanceof Error ? err.message : String(err)}` };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
738
648
|
}
|
|
649
|
+
|