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/editor.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
+
import { BaseTool } from './base-tool.js';
|
|
1
2
|
import { toVec3Object, toRotObject } from '../utils/normalize.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { DEFAULT_SCREENSHOT_RESOLUTION } from '../constants.js';
|
|
4
|
+
import { wasmIntegration } from '../wasm/index.js';
|
|
5
|
+
export class EditorTools extends BaseTool {
|
|
6
|
+
cameraBookmarks = new Map();
|
|
7
|
+
editorPreferences = new Map();
|
|
8
|
+
activeRecording;
|
|
8
9
|
async isInPIE() {
|
|
9
10
|
try {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
else:
|
|
16
|
-
print("PIE_STATE:False")
|
|
17
|
-
`.trim();
|
|
18
|
-
const resp = await this.bridge.executePython(pythonCmd);
|
|
19
|
-
const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
|
|
20
|
-
return out.includes('PIE_STATE:True');
|
|
11
|
+
const response = await this.sendAutomationRequest('check_pie_state', {}, { timeoutMs: 5000 });
|
|
12
|
+
if (response && response.success !== false) {
|
|
13
|
+
return response.isInPIE === true || response.result?.isInPIE === true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
21
16
|
}
|
|
22
17
|
catch {
|
|
23
18
|
return false;
|
|
@@ -26,70 +21,26 @@ else:
|
|
|
26
21
|
async ensureNotInPIE() {
|
|
27
22
|
if (await this.isInPIE()) {
|
|
28
23
|
await this.stopPlayInEditor();
|
|
29
|
-
// Wait a bit for PIE to fully stop
|
|
30
24
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31
25
|
}
|
|
32
26
|
}
|
|
33
|
-
async playInEditor() {
|
|
27
|
+
async playInEditor(timeoutMs = 30000) {
|
|
34
28
|
try {
|
|
35
|
-
// Set tick rate to match UI play (60 fps for game mode)
|
|
36
|
-
await this.bridge.executeConsoleCommand('t.MaxFPS 60');
|
|
37
|
-
// Try Python first using the modern LevelEditorSubsystem
|
|
38
29
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# Start PIE using LevelEditorSubsystem (modern approach)
|
|
43
|
-
les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
|
|
44
|
-
if les:
|
|
45
|
-
# Store initial state
|
|
46
|
-
was_playing = les.is_in_play_in_editor()
|
|
47
|
-
|
|
48
|
-
# Request PIE in the current viewport
|
|
49
|
-
les.editor_play_simulate()
|
|
50
|
-
|
|
51
|
-
# Wait for PIE to start with multiple checks
|
|
52
|
-
max_attempts = 10
|
|
53
|
-
for i in range(max_attempts):
|
|
54
|
-
time.sleep(0.2) # Wait 200ms between checks
|
|
55
|
-
is_playing = les.is_in_play_in_editor()
|
|
56
|
-
if is_playing and not was_playing:
|
|
57
|
-
# PIE has started
|
|
58
|
-
print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
|
|
59
|
-
break
|
|
60
|
-
else:
|
|
61
|
-
# If we've waited 2 seconds total and PIE hasn't started,
|
|
62
|
-
# but the command was sent, assume it will start
|
|
63
|
-
print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
|
|
64
|
-
else:
|
|
65
|
-
# If subsystem not available, report error
|
|
66
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
|
|
67
|
-
`.trim();
|
|
68
|
-
const resp = await this.bridge.executePython(pythonCmd);
|
|
69
|
-
const interpreted = interpretStandardResult(resp, {
|
|
70
|
-
successMessage: 'PIE started',
|
|
71
|
-
failureMessage: 'Failed to start PIE'
|
|
72
|
-
});
|
|
73
|
-
if (interpreted.success) {
|
|
74
|
-
const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
|
|
75
|
-
return { success: true, message: `PIE started (via ${method})` };
|
|
30
|
+
const response = await this.sendAutomationRequest('control_editor', { action: 'play' }, { timeoutMs });
|
|
31
|
+
if (response && response.success === true) {
|
|
32
|
+
return { success: true, message: response.message || 'PIE started' };
|
|
76
33
|
}
|
|
77
|
-
|
|
34
|
+
return { success: false, error: response?.error || response?.message || 'Failed to start PIE' };
|
|
78
35
|
}
|
|
79
36
|
catch (err) {
|
|
80
|
-
|
|
81
|
-
|
|
37
|
+
if (err.message && /time.*out/i.test(err.message)) {
|
|
38
|
+
return { success: false, error: `Timeout waiting for PIE to start: ${err.message}` };
|
|
39
|
+
}
|
|
40
|
+
await this.bridge.executeConsoleCommand('t.MaxFPS 60');
|
|
41
|
+
await this.bridge.executeConsoleCommand('PlayInViewport');
|
|
42
|
+
return { success: true, message: 'PIE start command sent' };
|
|
82
43
|
}
|
|
83
|
-
// Fallback to console command which is more reliable
|
|
84
|
-
await this.bridge.executeConsoleCommand('PlayInViewport');
|
|
85
|
-
// Wait a moment and verify PIE started
|
|
86
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
87
|
-
// Check if PIE is now active
|
|
88
|
-
const isPlaying = await this.isInPIE();
|
|
89
|
-
return {
|
|
90
|
-
success: true,
|
|
91
|
-
message: isPlaying ? 'PIE started successfully' : 'PIE start command sent (may take a moment)'
|
|
92
|
-
};
|
|
93
44
|
}
|
|
94
45
|
catch (err) {
|
|
95
46
|
return { success: false, error: `Failed to start PIE: ${err}` };
|
|
@@ -97,35 +48,20 @@ else:
|
|
|
97
48
|
}
|
|
98
49
|
async stopPlayInEditor() {
|
|
99
50
|
try {
|
|
100
|
-
// Try Python first using the modern LevelEditorSubsystem
|
|
101
51
|
try {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
|
|
109
|
-
else:
|
|
110
|
-
# If subsystem not available, report error
|
|
111
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
|
|
112
|
-
`.trim();
|
|
113
|
-
const resp = await this.bridge.executePython(pythonCmd);
|
|
114
|
-
const interpreted = interpretStandardResult(resp, {
|
|
115
|
-
successMessage: 'PIE stopped successfully',
|
|
116
|
-
failureMessage: 'Failed to stop PIE'
|
|
117
|
-
});
|
|
118
|
-
if (interpreted.success) {
|
|
119
|
-
const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
|
|
120
|
-
return { success: true, message: `PIE stopped via ${method}` };
|
|
121
|
-
}
|
|
122
|
-
if (interpreted.error) {
|
|
123
|
-
return { success: false, error: interpreted.error };
|
|
52
|
+
const response = await this.sendAutomationRequest('control_editor', { action: 'stop' }, { timeoutMs: 30000 });
|
|
53
|
+
if (response.success !== false) {
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
message: response.message || 'PIE stopped successfully'
|
|
57
|
+
};
|
|
124
58
|
}
|
|
125
|
-
return {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: response.error || response.message || 'Failed to stop PIE'
|
|
62
|
+
};
|
|
126
63
|
}
|
|
127
|
-
catch {
|
|
128
|
-
// Fallback to console command
|
|
64
|
+
catch (_pluginErr) {
|
|
129
65
|
await this.bridge.executeConsoleCommand('stop');
|
|
130
66
|
return { success: true, message: 'PIE stopped via console command' };
|
|
131
67
|
}
|
|
@@ -136,69 +72,44 @@ else:
|
|
|
136
72
|
}
|
|
137
73
|
async pausePlayInEditor() {
|
|
138
74
|
try {
|
|
139
|
-
|
|
140
|
-
await this.bridge.httpCall('/remote/object/call', 'PUT', {
|
|
141
|
-
objectPath: '/Script/Engine.Default__KismetSystemLibrary',
|
|
142
|
-
functionName: 'ExecuteConsoleCommand',
|
|
143
|
-
parameters: {
|
|
144
|
-
WorldContextObject: null,
|
|
145
|
-
Command: 'pause',
|
|
146
|
-
SpecificPlayer: null
|
|
147
|
-
},
|
|
148
|
-
generateTransaction: false
|
|
149
|
-
});
|
|
75
|
+
await this.bridge.executeConsoleCommand('pause');
|
|
150
76
|
return { success: true, message: 'PIE paused/resumed' };
|
|
151
77
|
}
|
|
152
78
|
catch (err) {
|
|
153
79
|
return { success: false, error: `Failed to pause PIE: ${err}` };
|
|
154
80
|
}
|
|
155
81
|
}
|
|
156
|
-
// Alias for consistency with naming convention
|
|
157
82
|
async pauseInEditor() {
|
|
158
83
|
return this.pausePlayInEditor();
|
|
159
84
|
}
|
|
160
85
|
async buildLighting() {
|
|
161
86
|
try {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
import unreal
|
|
165
|
-
import json
|
|
166
|
-
try:
|
|
167
|
-
# Use modern LevelEditorSubsystem API
|
|
168
|
-
les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
|
|
169
|
-
if les:
|
|
170
|
-
# build_light_maps(quality, with_reflection_captures)
|
|
171
|
-
les.build_light_maps(unreal.LightingBuildQuality.QUALITY_HIGH, True)
|
|
172
|
-
print('RESULT:' + json.dumps({'success': True, 'message': 'Lighting build started via LevelEditorSubsystem'}))
|
|
173
|
-
else:
|
|
174
|
-
# If subsystem not available, report error
|
|
175
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
|
|
176
|
-
except Exception as e:
|
|
177
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
178
|
-
`.trim();
|
|
179
|
-
const resp = await this.bridge.executePython(py);
|
|
180
|
-
const interpreted = interpretStandardResult(resp, {
|
|
181
|
-
successMessage: 'Lighting build started',
|
|
182
|
-
failureMessage: 'Failed to build lighting'
|
|
183
|
-
});
|
|
184
|
-
if (interpreted.success) {
|
|
185
|
-
return { success: true, message: interpreted.message };
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
success: false,
|
|
189
|
-
error: interpreted.error ?? 'Failed to build lighting',
|
|
190
|
-
details: bestEffortInterpretedText(interpreted)
|
|
191
|
-
};
|
|
87
|
+
await this.bridge.executeConsoleCommand('BuildLighting');
|
|
88
|
+
return { success: true, message: 'Lighting build started' };
|
|
192
89
|
}
|
|
193
90
|
catch (err) {
|
|
194
91
|
return { success: false, error: `Failed to build lighting: ${err}` };
|
|
195
92
|
}
|
|
196
93
|
}
|
|
94
|
+
async getViewportCameraInfo() {
|
|
95
|
+
try {
|
|
96
|
+
const resp = await this.sendAutomationRequest('control_editor', { action: 'get_camera' }, { timeoutMs: 3000 });
|
|
97
|
+
const result = resp?.result ?? resp;
|
|
98
|
+
const loc = result?.location ?? result?.camera?.location;
|
|
99
|
+
const rot = result?.rotation ?? result?.camera?.rotation;
|
|
100
|
+
const locArr = Array.isArray(loc) && loc.length === 3 ? [Number(loc[0]) || 0, Number(loc[1]) || 0, Number(loc[2]) || 0] : undefined;
|
|
101
|
+
const rotArr = Array.isArray(rot) && rot.length === 3 ? [Number(rot[0]) || 0, Number(rot[1]) || 0, Number(rot[2]) || 0] : undefined;
|
|
102
|
+
if (resp && resp.success !== false && locArr && rotArr) {
|
|
103
|
+
return { success: true, location: locArr, rotation: rotArr };
|
|
104
|
+
}
|
|
105
|
+
return { success: false, error: 'Failed to get camera information' };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return { success: false, error: `Camera query failed: ${err}` };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
197
111
|
async setViewportCamera(location, rotation) {
|
|
198
|
-
// Special handling for when both location and rotation are missing/invalid
|
|
199
|
-
// Allow rotation-only updates
|
|
200
112
|
if (location === null) {
|
|
201
|
-
// Explicit null is not allowed for location
|
|
202
113
|
throw new Error('Invalid location: null is not allowed');
|
|
203
114
|
}
|
|
204
115
|
if (location !== undefined && location !== null) {
|
|
@@ -206,14 +117,12 @@ except Exception as e:
|
|
|
206
117
|
if (!locObj) {
|
|
207
118
|
throw new Error('Invalid location: must be {x,y,z} or [x,y,z]');
|
|
208
119
|
}
|
|
209
|
-
|
|
210
|
-
const MAX_COORD = 1000000; // 1 million units is a reasonable max for UE
|
|
120
|
+
const MAX_COORD = 1000000;
|
|
211
121
|
locObj.x = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.x));
|
|
212
122
|
locObj.y = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.y));
|
|
213
123
|
locObj.z = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.z));
|
|
214
124
|
location = locObj;
|
|
215
125
|
}
|
|
216
|
-
// Validate rotation if provided
|
|
217
126
|
if (rotation !== undefined) {
|
|
218
127
|
if (rotation === null) {
|
|
219
128
|
throw new Error('Invalid rotation: null is not allowed');
|
|
@@ -222,107 +131,36 @@ except Exception as e:
|
|
|
222
131
|
if (!rotObj) {
|
|
223
132
|
throw new Error('Invalid rotation: must be {pitch,yaw,roll} or [pitch,yaw,roll]');
|
|
224
133
|
}
|
|
225
|
-
// Normalize rotation values to 0-360 range
|
|
226
134
|
rotObj.pitch = ((rotObj.pitch % 360) + 360) % 360;
|
|
227
135
|
rotObj.yaw = ((rotObj.yaw % 360) + 360) % 360;
|
|
228
136
|
rotObj.roll = ((rotObj.roll % 360) + 360) % 360;
|
|
229
137
|
rotation = rotObj;
|
|
230
138
|
}
|
|
231
139
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if
|
|
245
|
-
|
|
246
|
-
# Invalidate viewports to ensure visual update
|
|
247
|
-
try:
|
|
248
|
-
if les:
|
|
249
|
-
les.editor_invalidate_viewports()
|
|
250
|
-
except Exception:
|
|
251
|
-
pass
|
|
252
|
-
`.trim();
|
|
253
|
-
await this.bridge.executePython(pythonCmd);
|
|
254
|
-
return {
|
|
255
|
-
success: true,
|
|
256
|
-
message: 'Viewport camera positioned via UnrealEditorSubsystem'
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
catch {
|
|
260
|
-
// Fallback to camera speed control
|
|
261
|
-
await this.bridge.executeConsoleCommand('camspeed 4');
|
|
262
|
-
return {
|
|
263
|
-
success: true,
|
|
264
|
-
message: 'Camera speed set. Use debug camera (toggledebugcamera) for manual positioning'
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
else if (rotation) {
|
|
269
|
-
// Only rotation provided, try to set just rotation
|
|
270
|
-
try {
|
|
271
|
-
const pythonCmd = `
|
|
272
|
-
import unreal
|
|
273
|
-
# Use UnrealEditorSubsystem to read/write viewport camera
|
|
274
|
-
ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
|
|
275
|
-
les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
|
|
276
|
-
rotation = unreal.Rotator(${rotation.pitch}, ${rotation.yaw}, ${rotation.roll})
|
|
277
|
-
if ues:
|
|
278
|
-
info = ues.get_level_viewport_camera_info()
|
|
279
|
-
if info is not None:
|
|
280
|
-
current_location, _ = info
|
|
281
|
-
ues.set_level_viewport_camera_info(current_location, rotation)
|
|
282
|
-
try:
|
|
283
|
-
if les:
|
|
284
|
-
les.editor_invalidate_viewports()
|
|
285
|
-
except Exception:
|
|
286
|
-
pass
|
|
287
|
-
`.trim();
|
|
288
|
-
await this.bridge.executePython(pythonCmd);
|
|
289
|
-
return {
|
|
290
|
-
success: true,
|
|
291
|
-
message: 'Viewport camera rotation set via UnrealEditorSubsystem'
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
// Fallback
|
|
296
|
-
return {
|
|
297
|
-
success: true,
|
|
298
|
-
message: 'Camera rotation update attempted'
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
// Neither location nor rotation provided - this is valid, just no-op
|
|
304
|
-
return {
|
|
305
|
-
success: true,
|
|
306
|
-
message: 'No camera changes requested'
|
|
307
|
-
};
|
|
140
|
+
const locArray = location
|
|
141
|
+
? [(location.x ?? location[0] ?? 0), (location.y ?? location[1] ?? 0), (location.z ?? location[2] ?? 0)]
|
|
142
|
+
: [0, 0, 0];
|
|
143
|
+
const rotArray = rotation
|
|
144
|
+
? [(rotation.pitch ?? rotation[0] ?? 0), (rotation.yaw ?? rotation[1] ?? 0), (rotation.roll ?? rotation[2] ?? 0)]
|
|
145
|
+
: [0, 0, 0];
|
|
146
|
+
wasmIntegration.composeTransform(locArray, rotArray, [1, 1, 1]);
|
|
147
|
+
const resp = await this.sendAutomationRequest('control_editor', {
|
|
148
|
+
action: 'set_camera',
|
|
149
|
+
location: location,
|
|
150
|
+
rotation: rotation
|
|
151
|
+
}, { timeoutMs: 10000 });
|
|
152
|
+
if (resp && resp.success === true) {
|
|
153
|
+
return { success: true, message: resp.message || 'Camera set', location, rotation };
|
|
308
154
|
}
|
|
155
|
+
return { success: false, error: resp?.error || resp?.message || 'Failed to set camera' };
|
|
309
156
|
}
|
|
310
157
|
catch (err) {
|
|
311
|
-
return { success: false, error: `
|
|
158
|
+
return { success: false, error: `Camera control failed: ${err}` };
|
|
312
159
|
}
|
|
313
160
|
}
|
|
314
161
|
async setCameraSpeed(speed) {
|
|
315
162
|
try {
|
|
316
|
-
await this.bridge.
|
|
317
|
-
objectPath: '/Script/Engine.Default__KismetSystemLibrary',
|
|
318
|
-
functionName: 'ExecuteConsoleCommand',
|
|
319
|
-
parameters: {
|
|
320
|
-
WorldContextObject: null,
|
|
321
|
-
Command: `camspeed ${speed}`,
|
|
322
|
-
SpecificPlayer: null
|
|
323
|
-
},
|
|
324
|
-
generateTransaction: false
|
|
325
|
-
});
|
|
163
|
+
await this.bridge.executeConsoleCommand(`camspeed ${speed}`);
|
|
326
164
|
return { success: true, message: `Camera speed set to ${speed}` };
|
|
327
165
|
}
|
|
328
166
|
catch (err) {
|
|
@@ -331,21 +169,186 @@ if ues:
|
|
|
331
169
|
}
|
|
332
170
|
async setFOV(fov) {
|
|
333
171
|
try {
|
|
334
|
-
await this.bridge.
|
|
335
|
-
objectPath: '/Script/Engine.Default__KismetSystemLibrary',
|
|
336
|
-
functionName: 'ExecuteConsoleCommand',
|
|
337
|
-
parameters: {
|
|
338
|
-
WorldContextObject: null,
|
|
339
|
-
Command: `fov ${fov}`,
|
|
340
|
-
SpecificPlayer: null
|
|
341
|
-
},
|
|
342
|
-
generateTransaction: false
|
|
343
|
-
});
|
|
172
|
+
await this.bridge.executeConsoleCommand(`fov ${fov}`);
|
|
344
173
|
return { success: true, message: `FOV set to ${fov}` };
|
|
345
174
|
}
|
|
346
175
|
catch (err) {
|
|
347
176
|
return { success: false, error: `Failed to set FOV: ${err}` };
|
|
348
177
|
}
|
|
349
178
|
}
|
|
179
|
+
async takeScreenshot(filename, resolution) {
|
|
180
|
+
try {
|
|
181
|
+
if (resolution && !/^\d+x\d+$/.test(resolution)) {
|
|
182
|
+
return { success: false, error: 'Invalid resolution format. Use WxH (e.g. 1920x1080)' };
|
|
183
|
+
}
|
|
184
|
+
const sanitizedFilename = filename ? filename.replace(/[<>:*?"|]/g, '_') : `Screenshot_${Date.now()}`;
|
|
185
|
+
const resString = resolution || DEFAULT_SCREENSHOT_RESOLUTION;
|
|
186
|
+
const command = filename ? `highresshot ${resString} filename="${sanitizedFilename}"` : 'shot';
|
|
187
|
+
await this.bridge.executeConsoleCommand(command);
|
|
188
|
+
return {
|
|
189
|
+
success: true,
|
|
190
|
+
message: `Screenshot captured: ${sanitizedFilename}`,
|
|
191
|
+
filename: sanitizedFilename,
|
|
192
|
+
command
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
return { success: false, error: `Failed to take screenshot: ${err}` };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async resumePlayInEditor() {
|
|
200
|
+
try {
|
|
201
|
+
await this.bridge.executeConsoleCommand('pause');
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
message: 'PIE resume toggled via pause command'
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
return { success: false, error: `Failed to resume PIE: ${err}` };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async stepPIEFrame(steps = 1) {
|
|
212
|
+
const clampedSteps = Number.isFinite(steps) ? Math.max(1, Math.floor(steps)) : 1;
|
|
213
|
+
try {
|
|
214
|
+
for (let index = 0; index < clampedSteps; index += 1) {
|
|
215
|
+
await this.bridge.executeConsoleCommand('Step=1');
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
success: true,
|
|
219
|
+
message: `Advanced PIE by ${clampedSteps} frame(s)`,
|
|
220
|
+
steps: clampedSteps
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
return { success: false, error: `Failed to step PIE: ${err}` };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async startRecording(options) {
|
|
228
|
+
const startedAt = Date.now();
|
|
229
|
+
this.activeRecording = {
|
|
230
|
+
name: typeof options?.filename === 'string' ? options.filename.trim() : undefined,
|
|
231
|
+
options: options ? { ...options } : undefined,
|
|
232
|
+
startedAt
|
|
233
|
+
};
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
message: 'Recording session started',
|
|
237
|
+
recording: {
|
|
238
|
+
name: this.activeRecording.name,
|
|
239
|
+
startedAt,
|
|
240
|
+
options: this.activeRecording.options
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async stopRecording() {
|
|
245
|
+
if (!this.activeRecording) {
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
message: 'No active recording session to stop'
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const stoppedRecording = this.activeRecording;
|
|
252
|
+
this.activeRecording = undefined;
|
|
253
|
+
return {
|
|
254
|
+
success: true,
|
|
255
|
+
message: 'Recording session stopped',
|
|
256
|
+
recording: stoppedRecording
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async createCameraBookmark(name) {
|
|
260
|
+
const trimmedName = name.trim();
|
|
261
|
+
if (!trimmedName) {
|
|
262
|
+
return { success: false, error: 'bookmarkName is required' };
|
|
263
|
+
}
|
|
264
|
+
const cameraInfo = await this.getViewportCameraInfo();
|
|
265
|
+
if (!cameraInfo.success || !cameraInfo.location || !cameraInfo.rotation) {
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
error: cameraInfo.error || 'Failed to capture viewport camera'
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
this.cameraBookmarks.set(trimmedName, {
|
|
272
|
+
location: cameraInfo.location,
|
|
273
|
+
rotation: cameraInfo.rotation,
|
|
274
|
+
savedAt: Date.now()
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
message: `Bookmark '${trimmedName}' saved`,
|
|
279
|
+
bookmark: {
|
|
280
|
+
name: trimmedName,
|
|
281
|
+
location: cameraInfo.location,
|
|
282
|
+
rotation: cameraInfo.rotation
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async jumpToCameraBookmark(name) {
|
|
287
|
+
const trimmedName = name.trim();
|
|
288
|
+
if (!trimmedName) {
|
|
289
|
+
return { success: false, error: 'bookmarkName is required' };
|
|
290
|
+
}
|
|
291
|
+
const bookmark = this.cameraBookmarks.get(trimmedName);
|
|
292
|
+
if (!bookmark) {
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
error: `Bookmark '${trimmedName}' not found`
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
await this.setViewportCamera({ x: bookmark.location[0], y: bookmark.location[1], z: bookmark.location[2] }, { pitch: bookmark.rotation[0], yaw: bookmark.rotation[1], roll: bookmark.rotation[2] });
|
|
299
|
+
return {
|
|
300
|
+
success: true,
|
|
301
|
+
message: `Jumped to bookmark '${trimmedName}'`
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
async setEditorPreferences(category, preferences) {
|
|
305
|
+
const resolvedCategory = typeof category === 'string' && category.trim().length > 0 ? category.trim() : 'General';
|
|
306
|
+
const existing = this.editorPreferences.get(resolvedCategory) ?? {};
|
|
307
|
+
this.editorPreferences.set(resolvedCategory, { ...existing, ...preferences });
|
|
308
|
+
return {
|
|
309
|
+
success: true,
|
|
310
|
+
message: `Preferences stored for ${resolvedCategory}`,
|
|
311
|
+
preferences: this.editorPreferences.get(resolvedCategory)
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async setViewportResolution(width, height) {
|
|
315
|
+
try {
|
|
316
|
+
const clampedWidth = Math.max(320, Math.min(7680, width));
|
|
317
|
+
const clampedHeight = Math.max(240, Math.min(4320, height));
|
|
318
|
+
const command = `r.SetRes ${clampedWidth}x${clampedHeight}`;
|
|
319
|
+
await this.bridge.executeConsoleCommand(command);
|
|
320
|
+
return {
|
|
321
|
+
success: true,
|
|
322
|
+
message: `Viewport resolution set to ${clampedWidth}x${clampedHeight}`,
|
|
323
|
+
width: clampedWidth,
|
|
324
|
+
height: clampedHeight
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
return { success: false, error: `Failed to set viewport resolution: ${err}` };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async executeConsoleCommand(command) {
|
|
332
|
+
try {
|
|
333
|
+
if (!command || typeof command !== 'string') {
|
|
334
|
+
return { success: false, error: 'Invalid command: must be a non-empty string' };
|
|
335
|
+
}
|
|
336
|
+
if (command.length > 1000) {
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
error: `Command too long (${command.length} chars). Maximum is 1000 characters.`
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
const res = await this.bridge.executeConsoleCommand(command);
|
|
343
|
+
return {
|
|
344
|
+
success: true,
|
|
345
|
+
message: `Console command executed: ${command}`,
|
|
346
|
+
output: res
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
return { success: false, error: `Failed to execute console command: ${err}` };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
350
353
|
}
|
|
351
354
|
//# sourceMappingURL=editor.js.map
|
package/dist/tools/engine.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
|
+
import { AutomationBridge } from '../automation/index.js';
|
|
2
3
|
export declare class EngineTools {
|
|
3
|
-
private
|
|
4
|
+
private automationBridge?;
|
|
4
5
|
private env;
|
|
5
|
-
constructor(
|
|
6
|
+
constructor(_bridge: UnrealBridge, automationBridge?: AutomationBridge | undefined);
|
|
7
|
+
setAutomationBridge(automationBridge?: AutomationBridge): void;
|
|
6
8
|
launchEditor(params?: {
|
|
7
9
|
editorExe?: string;
|
|
8
10
|
projectPath?: string;
|
|
@@ -19,12 +21,16 @@ export declare class EngineTools {
|
|
|
19
21
|
}>;
|
|
20
22
|
quitEditor(): Promise<{
|
|
21
23
|
success: boolean;
|
|
24
|
+
error: string;
|
|
22
25
|
message: string;
|
|
23
|
-
error?: undefined;
|
|
24
26
|
} | {
|
|
25
27
|
success: boolean;
|
|
26
|
-
error:
|
|
28
|
+
error: any;
|
|
27
29
|
message?: undefined;
|
|
30
|
+
} | {
|
|
31
|
+
success: boolean;
|
|
32
|
+
message: string;
|
|
33
|
+
error?: undefined;
|
|
28
34
|
}>;
|
|
29
35
|
}
|
|
30
36
|
//# sourceMappingURL=engine.d.ts.map
|
package/dist/tools/engine.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { loadEnv } from '../types/env.js';
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
export class EngineTools {
|
|
4
|
-
|
|
4
|
+
automationBridge;
|
|
5
5
|
env = loadEnv();
|
|
6
|
-
constructor(
|
|
7
|
-
this.
|
|
6
|
+
constructor(_bridge, automationBridge) {
|
|
7
|
+
this.automationBridge = automationBridge;
|
|
8
|
+
}
|
|
9
|
+
setAutomationBridge(automationBridge) {
|
|
10
|
+
this.automationBridge = automationBridge;
|
|
8
11
|
}
|
|
9
12
|
async launchEditor(params) {
|
|
10
13
|
const exe = params?.editorExe || this.env.UE_EDITOR_EXE;
|
|
@@ -23,9 +26,14 @@ export class EngineTools {
|
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
async quitEditor() {
|
|
29
|
+
if (!this.automationBridge) {
|
|
30
|
+
return { success: false, error: 'AUTOMATION_BRIDGE_UNAVAILABLE', message: 'Automation bridge is not available for quit_editor' };
|
|
31
|
+
}
|
|
26
32
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
const resp = await this.automationBridge.sendAutomationRequest('quit_editor', {});
|
|
34
|
+
if (resp && resp.success === false) {
|
|
35
|
+
return { success: false, error: resp.error || resp.message || 'Quit request failed' };
|
|
36
|
+
}
|
|
29
37
|
return { success: true, message: 'Quit command sent' };
|
|
30
38
|
}
|
|
31
39
|
catch (err) {
|