unreal-engine-mcp-server 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +26 -0
- package/.env.production +38 -7
- package/.eslintrc.json +0 -54
- package/.eslintrc.override.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
- package/.github/copilot-instructions.md +478 -45
- package/.github/dependabot.yml +19 -0
- package/.github/labeler.yml +24 -0
- package/.github/labels.yml +70 -0
- package/.github/pull_request_template.md +42 -0
- package/.github/release-drafter.yml +148 -0
- package/.github/workflows/auto-merge.yml +38 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/dependency-review.yml +17 -0
- package/.github/workflows/gemini-issue-triage.yml +172 -0
- package/.github/workflows/greetings.yml +23 -0
- package/.github/workflows/labeler.yml +16 -0
- package/.github/workflows/links.yml +80 -0
- package/.github/workflows/pr-size-labeler.yml +137 -0
- package/.github/workflows/publish-mcp.yml +12 -7
- package/.github/workflows/release-drafter.yml +23 -0
- package/.github/workflows/release.yml +112 -0
- package/.github/workflows/semantic-pull-request.yml +35 -0
- package/.github/workflows/smoke-test.yml +36 -0
- package/.github/workflows/stale.yml +28 -0
- package/CHANGELOG.md +269 -22
- package/CONTRIBUTING.md +140 -0
- package/README.md +166 -72
- package/claude_desktop_config_example.json +7 -6
- package/dist/automation/bridge.d.ts +50 -0
- package/dist/automation/bridge.js +452 -0
- package/dist/automation/connection-manager.d.ts +23 -0
- package/dist/automation/connection-manager.js +107 -0
- package/dist/automation/handshake.d.ts +11 -0
- package/dist/automation/handshake.js +89 -0
- package/dist/automation/index.d.ts +3 -0
- package/dist/automation/index.js +3 -0
- package/dist/automation/message-handler.d.ts +12 -0
- package/dist/automation/message-handler.js +149 -0
- package/dist/automation/request-tracker.d.ts +25 -0
- package/dist/automation/request-tracker.js +98 -0
- package/dist/automation/types.d.ts +130 -0
- package/dist/automation/types.js +2 -0
- package/dist/cli.js +32 -5
- package/dist/config.d.ts +27 -0
- package/dist/config.js +60 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +12 -0
- package/dist/graphql/resolvers.d.ts +268 -0
- package/dist/graphql/resolvers.js +743 -0
- package/dist/graphql/schema.d.ts +5 -0
- package/dist/graphql/schema.js +437 -0
- package/dist/graphql/server.d.ts +26 -0
- package/dist/graphql/server.js +115 -0
- package/dist/graphql/types.d.ts +7 -0
- package/dist/graphql/types.js +2 -0
- package/dist/handlers/resource-handlers.d.ts +20 -0
- package/dist/handlers/resource-handlers.js +180 -0
- package/dist/index.d.ts +31 -18
- package/dist/index.js +119 -604
- package/dist/prompts/index.js +4 -4
- package/dist/resources/actors.d.ts +17 -12
- package/dist/resources/actors.js +56 -76
- package/dist/resources/assets.d.ts +6 -14
- package/dist/resources/assets.js +115 -147
- package/dist/resources/levels.d.ts +13 -13
- package/dist/resources/levels.js +25 -34
- package/dist/server/resource-registry.d.ts +20 -0
- package/dist/server/resource-registry.js +37 -0
- package/dist/server/tool-registry.d.ts +23 -0
- package/dist/server/tool-registry.js +322 -0
- package/dist/server-setup.d.ts +21 -0
- package/dist/server-setup.js +111 -0
- package/dist/services/health-monitor.d.ts +34 -0
- package/dist/services/health-monitor.js +105 -0
- package/dist/services/metrics-server.d.ts +11 -0
- package/dist/services/metrics-server.js +105 -0
- package/dist/tools/actors.d.ts +147 -9
- package/dist/tools/actors.js +350 -311
- package/dist/tools/animation.d.ts +135 -4
- package/dist/tools/animation.js +510 -411
- package/dist/tools/assets.d.ts +117 -19
- package/dist/tools/assets.js +259 -284
- package/dist/tools/audio.d.ts +102 -42
- package/dist/tools/audio.js +272 -685
- package/dist/tools/base-tool.d.ts +17 -0
- package/dist/tools/base-tool.js +46 -0
- package/dist/tools/behavior-tree.d.ts +94 -0
- package/dist/tools/behavior-tree.js +39 -0
- package/dist/tools/blueprint/helpers.d.ts +29 -0
- package/dist/tools/blueprint/helpers.js +182 -0
- package/dist/tools/blueprint.d.ts +228 -118
- package/dist/tools/blueprint.js +685 -832
- package/dist/tools/consolidated-tool-definitions.d.ts +5475 -1627
- package/dist/tools/consolidated-tool-definitions.js +829 -482
- package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
- package/dist/tools/consolidated-tool-handlers.js +211 -1009
- package/dist/tools/debug.d.ts +143 -85
- package/dist/tools/debug.js +234 -180
- package/dist/tools/dynamic-handler-registry.d.ts +11 -0
- package/dist/tools/dynamic-handler-registry.js +101 -0
- package/dist/tools/editor.d.ts +139 -18
- package/dist/tools/editor.js +239 -244
- package/dist/tools/engine.d.ts +10 -4
- package/dist/tools/engine.js +13 -5
- package/dist/tools/environment.d.ts +36 -0
- package/dist/tools/environment.js +267 -0
- package/dist/tools/foliage.d.ts +105 -14
- package/dist/tools/foliage.js +219 -331
- package/dist/tools/handlers/actor-handlers.d.ts +3 -0
- package/dist/tools/handlers/actor-handlers.js +232 -0
- package/dist/tools/handlers/animation-handlers.d.ts +3 -0
- package/dist/tools/handlers/animation-handlers.js +185 -0
- package/dist/tools/handlers/argument-helper.d.ts +16 -0
- package/dist/tools/handlers/argument-helper.js +80 -0
- package/dist/tools/handlers/asset-handlers.d.ts +3 -0
- package/dist/tools/handlers/asset-handlers.js +496 -0
- package/dist/tools/handlers/audio-handlers.d.ts +3 -0
- package/dist/tools/handlers/audio-handlers.js +166 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
- package/dist/tools/handlers/blueprint-handlers.js +358 -0
- package/dist/tools/handlers/common-handlers.d.ts +14 -0
- package/dist/tools/handlers/common-handlers.js +56 -0
- package/dist/tools/handlers/editor-handlers.d.ts +3 -0
- package/dist/tools/handlers/editor-handlers.js +119 -0
- package/dist/tools/handlers/effect-handlers.d.ts +3 -0
- package/dist/tools/handlers/effect-handlers.js +171 -0
- package/dist/tools/handlers/environment-handlers.d.ts +3 -0
- package/dist/tools/handlers/environment-handlers.js +170 -0
- package/dist/tools/handlers/graph-handlers.d.ts +3 -0
- package/dist/tools/handlers/graph-handlers.js +90 -0
- package/dist/tools/handlers/input-handlers.d.ts +3 -0
- package/dist/tools/handlers/input-handlers.js +21 -0
- package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
- package/dist/tools/handlers/inspect-handlers.js +383 -0
- package/dist/tools/handlers/level-handlers.d.ts +3 -0
- package/dist/tools/handlers/level-handlers.js +237 -0
- package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
- package/dist/tools/handlers/lighting-handlers.js +144 -0
- package/dist/tools/handlers/performance-handlers.d.ts +3 -0
- package/dist/tools/handlers/performance-handlers.js +130 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
- package/dist/tools/handlers/pipeline-handlers.js +110 -0
- package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
- package/dist/tools/handlers/sequence-handlers.js +376 -0
- package/dist/tools/handlers/system-handlers.d.ts +4 -0
- package/dist/tools/handlers/system-handlers.js +506 -0
- package/dist/tools/input.d.ts +19 -0
- package/dist/tools/input.js +89 -0
- package/dist/tools/introspection.d.ts +103 -40
- package/dist/tools/introspection.js +425 -568
- package/dist/tools/landscape.d.ts +97 -36
- package/dist/tools/landscape.js +280 -409
- package/dist/tools/level.d.ts +130 -10
- package/dist/tools/level.js +639 -675
- package/dist/tools/lighting.d.ts +77 -38
- package/dist/tools/lighting.js +441 -943
- package/dist/tools/logs.d.ts +45 -0
- package/dist/tools/logs.js +210 -0
- package/dist/tools/materials.d.ts +91 -24
- package/dist/tools/materials.js +190 -118
- package/dist/tools/niagara.d.ts +149 -39
- package/dist/tools/niagara.js +232 -182
- package/dist/tools/performance.d.ts +27 -12
- package/dist/tools/performance.js +204 -122
- package/dist/tools/physics.d.ts +32 -77
- package/dist/tools/physics.js +171 -582
- package/dist/tools/property-dictionary.d.ts +13 -0
- package/dist/tools/property-dictionary.js +82 -0
- package/dist/tools/sequence.d.ts +73 -48
- package/dist/tools/sequence.js +196 -748
- package/dist/tools/tool-definition-utils.d.ts +59 -0
- package/dist/tools/tool-definition-utils.js +35 -0
- package/dist/tools/ui.d.ts +66 -34
- package/dist/tools/ui.js +134 -214
- package/dist/types/env.d.ts +0 -3
- package/dist/types/env.js +0 -7
- package/dist/types/tool-interfaces.d.ts +898 -0
- package/dist/types/tool-interfaces.js +2 -0
- package/dist/types/tool-types.d.ts +195 -11
- package/dist/types/tool-types.js +0 -4
- package/dist/unreal-bridge.d.ts +24 -131
- package/dist/unreal-bridge.js +364 -1506
- package/dist/utils/command-validator.d.ts +9 -0
- package/dist/utils/command-validator.js +67 -0
- package/dist/utils/elicitation.d.ts +1 -1
- package/dist/utils/elicitation.js +12 -15
- package/dist/utils/error-handler.d.ts +2 -51
- package/dist/utils/error-handler.js +11 -87
- package/dist/utils/ini-reader.d.ts +3 -0
- package/dist/utils/ini-reader.js +69 -0
- package/dist/utils/logger.js +9 -6
- package/dist/utils/normalize.d.ts +3 -0
- package/dist/utils/normalize.js +56 -0
- package/dist/utils/response-factory.d.ts +7 -0
- package/dist/utils/response-factory.js +33 -0
- package/dist/utils/response-validator.d.ts +3 -24
- package/dist/utils/response-validator.js +130 -81
- package/dist/utils/result-helpers.d.ts +4 -5
- package/dist/utils/result-helpers.js +15 -16
- package/dist/utils/safe-json.js +5 -11
- package/dist/utils/unreal-command-queue.d.ts +24 -0
- package/dist/utils/unreal-command-queue.js +120 -0
- package/dist/utils/validation.d.ts +0 -40
- package/dist/utils/validation.js +1 -78
- package/dist/wasm/index.d.ts +70 -0
- package/dist/wasm/index.js +535 -0
- package/docs/GraphQL-API.md +888 -0
- package/docs/Migration-Guide-v0.5.0.md +692 -0
- package/docs/Roadmap.md +53 -0
- package/docs/WebAssembly-Integration.md +628 -0
- package/docs/editor-plugin-extension.md +370 -0
- package/docs/handler-mapping.md +242 -0
- package/docs/native-automation-progress.md +128 -0
- package/docs/testing-guide.md +423 -0
- package/mcp-config-example.json +6 -6
- package/package.json +60 -27
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
- package/scripts/check-unreal-connection.mjs +19 -0
- package/scripts/clean-tmp.js +23 -0
- package/scripts/patch-wasm.js +26 -0
- package/scripts/run-all-tests.mjs +131 -0
- package/scripts/smoke-test.ts +94 -0
- package/scripts/sync-mcp-plugin.js +143 -0
- package/scripts/test-no-plugin-alternates.mjs +113 -0
- package/scripts/validate-server.js +46 -0
- package/scripts/verify-automation-bridge.js +200 -0
- package/server.json +57 -21
- package/src/automation/bridge.ts +558 -0
- package/src/automation/connection-manager.ts +130 -0
- package/src/automation/handshake.ts +99 -0
- package/src/automation/index.ts +2 -0
- package/src/automation/message-handler.ts +167 -0
- package/src/automation/request-tracker.ts +123 -0
- package/src/automation/types.ts +107 -0
- package/src/cli.ts +33 -6
- package/src/config.ts +73 -0
- package/src/constants.ts +12 -0
- package/src/graphql/resolvers.ts +1010 -0
- package/src/graphql/schema.ts +452 -0
- package/src/graphql/server.ts +154 -0
- package/src/graphql/types.ts +7 -0
- package/src/handlers/resource-handlers.ts +186 -0
- package/src/index.ts +152 -649
- package/src/prompts/index.ts +4 -4
- package/src/resources/actors.ts +58 -76
- package/src/resources/assets.ts +147 -134
- package/src/resources/levels.ts +28 -33
- package/src/server/resource-registry.ts +47 -0
- package/src/server/tool-registry.ts +354 -0
- package/src/server-setup.ts +148 -0
- package/src/services/health-monitor.ts +132 -0
- package/src/services/metrics-server.ts +142 -0
- package/src/tools/actors.ts +417 -322
- package/src/tools/animation.ts +671 -461
- package/src/tools/assets.ts +353 -289
- package/src/tools/audio.ts +323 -766
- package/src/tools/base-tool.ts +52 -0
- package/src/tools/behavior-tree.ts +45 -0
- package/src/tools/blueprint/helpers.ts +189 -0
- package/src/tools/blueprint.ts +787 -965
- package/src/tools/consolidated-tool-definitions.ts +993 -500
- package/src/tools/consolidated-tool-handlers.ts +272 -1122
- package/src/tools/debug.ts +292 -187
- package/src/tools/dynamic-handler-registry.ts +151 -0
- package/src/tools/editor.ts +309 -246
- package/src/tools/engine.ts +14 -3
- package/src/tools/environment.ts +287 -0
- package/src/tools/foliage.ts +314 -379
- package/src/tools/handlers/actor-handlers.ts +271 -0
- package/src/tools/handlers/animation-handlers.ts +237 -0
- package/src/tools/handlers/argument-helper.ts +142 -0
- package/src/tools/handlers/asset-handlers.ts +532 -0
- package/src/tools/handlers/audio-handlers.ts +194 -0
- package/src/tools/handlers/blueprint-handlers.ts +380 -0
- package/src/tools/handlers/common-handlers.ts +87 -0
- package/src/tools/handlers/editor-handlers.ts +123 -0
- package/src/tools/handlers/effect-handlers.ts +220 -0
- package/src/tools/handlers/environment-handlers.ts +183 -0
- package/src/tools/handlers/graph-handlers.ts +116 -0
- package/src/tools/handlers/input-handlers.ts +28 -0
- package/src/tools/handlers/inspect-handlers.ts +450 -0
- package/src/tools/handlers/level-handlers.ts +252 -0
- package/src/tools/handlers/lighting-handlers.ts +147 -0
- package/src/tools/handlers/performance-handlers.ts +132 -0
- package/src/tools/handlers/pipeline-handlers.ts +127 -0
- package/src/tools/handlers/sequence-handlers.ts +415 -0
- package/src/tools/handlers/system-handlers.ts +564 -0
- package/src/tools/input.ts +101 -0
- package/src/tools/introspection.ts +493 -584
- package/src/tools/landscape.ts +394 -489
- package/src/tools/level.ts +752 -694
- package/src/tools/lighting.ts +583 -984
- package/src/tools/logs.ts +219 -0
- package/src/tools/materials.ts +231 -121
- package/src/tools/niagara.ts +293 -168
- package/src/tools/performance.ts +320 -168
- package/src/tools/physics.ts +268 -613
- package/src/tools/property-dictionary.ts +98 -0
- package/src/tools/sequence.ts +255 -815
- package/src/tools/tool-definition-utils.ts +35 -0
- package/src/tools/ui.ts +207 -283
- package/src/types/env.ts +0 -10
- package/src/types/tool-interfaces.ts +250 -0
- package/src/types/tool-types.ts +250 -13
- package/src/unreal-bridge.ts +460 -1550
- package/src/utils/command-validator.ts +75 -0
- package/src/utils/elicitation.ts +10 -7
- package/src/utils/error-handler.ts +14 -90
- package/src/utils/ini-reader.ts +86 -0
- package/src/utils/logger.ts +8 -3
- package/src/utils/normalize.ts +60 -0
- package/src/utils/response-factory.ts +39 -0
- package/src/utils/response-validator.ts +176 -56
- package/src/utils/result-helpers.ts +21 -19
- package/src/utils/safe-json.ts +14 -11
- package/src/utils/unreal-command-queue.ts +152 -0
- package/src/utils/validation.ts +4 -1
- package/src/wasm/index.ts +838 -0
- package/test-server.mjs +100 -0
- package/tests/run-unreal-tool-tests.mjs +242 -14
- package/tests/test-animation.mjs +44 -0
- package/tests/test-asset-advanced.mjs +82 -0
- package/tests/test-asset-errors.mjs +35 -0
- package/tests/test-audio.mjs +219 -0
- package/tests/test-automation-timeouts.mjs +98 -0
- package/tests/test-behavior-tree.mjs +261 -0
- package/tests/test-blueprint-events.mjs +35 -0
- package/tests/test-blueprint-graph.mjs +79 -0
- package/tests/test-blueprint.mjs +577 -0
- package/tests/test-client-mode.mjs +86 -0
- package/tests/test-console-command.mjs +56 -0
- package/tests/test-control-actor.mjs +425 -0
- package/tests/test-control-editor.mjs +80 -0
- package/tests/test-extra-tools.mjs +38 -0
- package/tests/test-graphql.mjs +322 -0
- package/tests/test-inspect.mjs +72 -0
- package/tests/test-landscape.mjs +60 -0
- package/tests/test-manage-asset.mjs +438 -0
- package/tests/test-manage-level.mjs +70 -0
- package/tests/test-materials.mjs +356 -0
- package/tests/test-niagara.mjs +185 -0
- package/tests/test-no-inline-python.mjs +122 -0
- package/tests/test-plugin-handshake.mjs +82 -0
- package/tests/test-render.mjs +33 -0
- package/tests/test-runner.mjs +933 -0
- package/tests/test-search-assets.mjs +66 -0
- package/tests/test-sequence.mjs +68 -0
- package/tests/test-system.mjs +57 -0
- package/tests/test-wasm.mjs +193 -0
- package/tests/test-world-partition.mjs +215 -0
- package/tsconfig.json +3 -3
- package/wasm/Cargo.lock +363 -0
- package/wasm/Cargo.toml +42 -0
- package/wasm/LICENSE +21 -0
- package/wasm/README.md +253 -0
- package/wasm/src/dependency_resolver.rs +377 -0
- package/wasm/src/lib.rs +153 -0
- package/wasm/src/property_parser.rs +271 -0
- package/wasm/src/transform_math.rs +396 -0
- package/wasm/tests/integration.rs +109 -0
- package/.github/workflows/smithery-build.yml +0 -29
- package/dist/tools/build_environment_advanced.d.ts +0 -65
- package/dist/tools/build_environment_advanced.js +0 -633
- package/dist/tools/rc.d.ts +0 -110
- package/dist/tools/rc.js +0 -437
- package/dist/tools/visual.d.ts +0 -40
- package/dist/tools/visual.js +0 -282
- package/dist/utils/http.d.ts +0 -6
- package/dist/utils/http.js +0 -151
- package/dist/utils/python-output.d.ts +0 -18
- package/dist/utils/python-output.js +0 -290
- package/dist/utils/python.d.ts +0 -2
- package/dist/utils/python.js +0 -4
- package/dist/utils/stdio-redirect.d.ts +0 -2
- package/dist/utils/stdio-redirect.js +0 -20
- package/docs/unreal-tool-test-cases.md +0 -572
- package/smithery.yaml +0 -29
- package/src/tools/build_environment_advanced.ts +0 -732
- package/src/tools/rc.ts +0 -515
- package/src/tools/visual.ts +0 -281
- package/src/utils/http.ts +0 -187
- package/src/utils/python-output.ts +0 -351
- package/src/utils/python.ts +0 -3
- package/src/utils/stdio-redirect.ts +0 -18
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
|
+
import { AutomationBridge } from '../automation/index.js';
|
|
2
3
|
import { Logger } from '../utils/logger.js';
|
|
3
|
-
import {
|
|
4
|
+
import { lookupPropertyMetadata, normalizeDictionaryKey, PropertyDictionaryEntry } from './property-dictionary.js';
|
|
5
|
+
|
|
6
|
+
export interface ObjectSummary {
|
|
7
|
+
name?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
parent?: string;
|
|
11
|
+
tags?: string[];
|
|
12
|
+
propertyCount: number;
|
|
13
|
+
curatedPropertyCount: number;
|
|
14
|
+
filteredPropertyCount: number;
|
|
15
|
+
categories?: Record<string, number>;
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
export interface ObjectInfo {
|
|
6
|
-
class
|
|
7
|
-
name
|
|
8
|
-
path
|
|
19
|
+
class?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
path?: string;
|
|
9
22
|
properties: PropertyInfo[];
|
|
10
23
|
functions?: FunctionInfo[];
|
|
11
24
|
parent?: string;
|
|
12
25
|
interfaces?: string[];
|
|
13
26
|
flags?: string[];
|
|
27
|
+
summary?: ObjectSummary;
|
|
28
|
+
filteredProperties?: string[];
|
|
29
|
+
tags?: string[];
|
|
30
|
+
original?: any;
|
|
14
31
|
}
|
|
15
32
|
|
|
16
33
|
export interface PropertyInfo {
|
|
@@ -21,6 +38,10 @@ export interface PropertyInfo {
|
|
|
21
38
|
metadata?: Record<string, any>;
|
|
22
39
|
category?: string;
|
|
23
40
|
tooltip?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
displayValue?: string;
|
|
43
|
+
dictionaryEntry?: PropertyDictionaryEntry;
|
|
44
|
+
isReadOnly?: boolean;
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
export interface FunctionInfo {
|
|
@@ -43,8 +64,28 @@ export class IntrospectionTools {
|
|
|
43
64
|
private objectCache = new Map<string, ObjectInfo>();
|
|
44
65
|
private retryAttempts = 3;
|
|
45
66
|
private retryDelay = 1000;
|
|
46
|
-
|
|
47
|
-
|
|
67
|
+
|
|
68
|
+
private readonly propertyFilterPatterns: RegExp[] = [
|
|
69
|
+
/internal/i,
|
|
70
|
+
/transient/i,
|
|
71
|
+
/^temp/i,
|
|
72
|
+
/^bhidden/i,
|
|
73
|
+
/renderstate/i,
|
|
74
|
+
/previewonly/i,
|
|
75
|
+
/deprecated/i
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
private readonly ignoredPropertyKeys = new Set<string>([
|
|
79
|
+
'assetimportdata',
|
|
80
|
+
'blueprintcreatedcomponents',
|
|
81
|
+
'componentreplicator',
|
|
82
|
+
'componentoverrides',
|
|
83
|
+
'actorcomponenttags'
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
constructor(private bridge: UnrealBridge, private automationBridge?: AutomationBridge) { }
|
|
87
|
+
|
|
88
|
+
setAutomationBridge(automationBridge?: AutomationBridge) { this.automationBridge = automationBridge; }
|
|
48
89
|
|
|
49
90
|
/**
|
|
50
91
|
* Execute with retry logic for transient failures
|
|
@@ -54,57 +95,23 @@ export class IntrospectionTools {
|
|
|
54
95
|
operationName: string
|
|
55
96
|
): Promise<T> {
|
|
56
97
|
let lastError: any;
|
|
57
|
-
|
|
98
|
+
|
|
58
99
|
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
|
|
59
100
|
try {
|
|
60
101
|
return await operation();
|
|
61
102
|
} catch (error: any) {
|
|
62
103
|
lastError = error;
|
|
63
104
|
this.log.warn(`${operationName} attempt ${attempt} failed: ${error.message || error}`);
|
|
64
|
-
|
|
105
|
+
|
|
65
106
|
if (attempt < this.retryAttempts) {
|
|
66
|
-
await new Promise(resolve =>
|
|
107
|
+
await new Promise(resolve =>
|
|
67
108
|
setTimeout(resolve, this.retryDelay * attempt)
|
|
68
109
|
);
|
|
69
110
|
}
|
|
70
111
|
}
|
|
71
112
|
}
|
|
72
|
-
|
|
73
|
-
throw lastError;
|
|
74
|
-
}
|
|
75
113
|
|
|
76
|
-
|
|
77
|
-
* Parse Python execution result with better error handling
|
|
78
|
-
*/
|
|
79
|
-
private parsePythonResult(resp: any, operationName: string): any {
|
|
80
|
-
const interpreted = interpretStandardResult(resp, {
|
|
81
|
-
successMessage: `${operationName} succeeded`,
|
|
82
|
-
failureMessage: `${operationName} failed`
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
if (interpreted.success) {
|
|
86
|
-
return {
|
|
87
|
-
...interpreted.payload,
|
|
88
|
-
success: true
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const output = bestEffortInterpretedText(interpreted) ?? '';
|
|
93
|
-
if (output) {
|
|
94
|
-
this.log.error(`Failed to parse ${operationName} result: ${output}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (output.includes('ModuleNotFoundError')) {
|
|
98
|
-
return { success: false, error: 'Reflection module not available.' };
|
|
99
|
-
}
|
|
100
|
-
if (output.includes('AttributeError')) {
|
|
101
|
-
return { success: false, error: 'Reflection API method not found. Check Unreal Engine version compatibility.' };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
error: `${interpreted.error ?? `${operationName} did not return a valid result`}: ${output.substring(0, 200)}`
|
|
107
|
-
};
|
|
114
|
+
throw lastError;
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
/**
|
|
@@ -134,6 +141,197 @@ export class IntrospectionTools {
|
|
|
134
141
|
return value;
|
|
135
142
|
}
|
|
136
143
|
|
|
144
|
+
private isPlainObject(value: any): value is Record<string, any> {
|
|
145
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private isLikelyPropertyDescriptor(value: any): boolean {
|
|
149
|
+
if (!this.isPlainObject(value)) return false;
|
|
150
|
+
if (typeof value.name === 'string' && ('value' in value || 'type' in value)) return true;
|
|
151
|
+
if ('propertyName' in value && ('currentValue' in value || 'defaultValue' in value)) return true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private shouldFilterProperty(name: string, value: any, flags?: string[], detailed = false): boolean {
|
|
156
|
+
if (detailed) return false;
|
|
157
|
+
if (!name) return true;
|
|
158
|
+
const normalized = normalizeDictionaryKey(name);
|
|
159
|
+
if (this.ignoredPropertyKeys.has(normalized)) return true;
|
|
160
|
+
for (const pattern of this.propertyFilterPatterns) {
|
|
161
|
+
if (pattern.test(name)) return true;
|
|
162
|
+
}
|
|
163
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
164
|
+
if (this.isPlainObject(value) && Object.keys(value).length === 0) return true;
|
|
165
|
+
if (flags?.some((f) => /deprecated/i.test(f))) return true;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private formatDisplayValue(value: any, type: string): string {
|
|
170
|
+
if (value === null || value === undefined) return 'None';
|
|
171
|
+
if (typeof value === 'string') return value.length > 120 ? `${value.slice(0, 117)}...` : value;
|
|
172
|
+
if (typeof value === 'number' || typeof value === 'boolean') return `${value}`;
|
|
173
|
+
if (Array.isArray(value)) {
|
|
174
|
+
const preview = value.slice(0, 5).map((entry) => this.formatDisplayValue(entry, typeof entry));
|
|
175
|
+
const suffix = value.length > 5 ? `, … (+${value.length - 5})` : '';
|
|
176
|
+
return `[${preview.join(', ')}${suffix}]`;
|
|
177
|
+
}
|
|
178
|
+
if (this.isPlainObject(value)) {
|
|
179
|
+
const keys = Object.keys(value);
|
|
180
|
+
if (['vector', 'rotator', 'transform'].some((token) => type.toLowerCase().includes(token))) {
|
|
181
|
+
const printable = Object.entries(value)
|
|
182
|
+
.map(([k, v]) => `${k}: ${this.formatDisplayValue(v, typeof v)}`)
|
|
183
|
+
.join(', ');
|
|
184
|
+
return `{ ${printable} }`;
|
|
185
|
+
}
|
|
186
|
+
const preview = keys.slice(0, 5).map((k) => `${k}: ${this.formatDisplayValue(value[k], typeof value[k])}`);
|
|
187
|
+
const suffix = keys.length > 5 ? `, … (+${keys.length - 5} keys)` : '';
|
|
188
|
+
return `{ ${preview.join(', ')}${suffix} }`;
|
|
189
|
+
}
|
|
190
|
+
return String(value);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private normalizePropertyEntry(entry: any, detailed = false): PropertyInfo | null {
|
|
194
|
+
if (!entry) return null;
|
|
195
|
+
const name: string = entry.name ?? entry.propertyName ?? entry.key ?? '';
|
|
196
|
+
if (!name) return null;
|
|
197
|
+
|
|
198
|
+
const candidateType = entry.type ?? entry.propertyType ?? (entry.value !== undefined ? typeof entry.value : undefined);
|
|
199
|
+
const type = typeof candidateType === 'string' && candidateType.length > 0 ? candidateType : 'unknown';
|
|
200
|
+
const rawValue = entry.value ?? entry.currentValue ?? entry.defaultValue ?? entry.data ?? entry;
|
|
201
|
+
const value = this.convertPropertyValue(rawValue, type);
|
|
202
|
+
const flags: string[] | undefined = entry.flags ?? entry.attributes;
|
|
203
|
+
const metadata: Record<string, any> | undefined = entry.metadata ?? entry.annotations;
|
|
204
|
+
const filtered = this.shouldFilterProperty(name, value, flags, detailed);
|
|
205
|
+
const dictionaryEntry = lookupPropertyMetadata(name);
|
|
206
|
+
const propertyInfo: PropertyInfo = {
|
|
207
|
+
name,
|
|
208
|
+
type,
|
|
209
|
+
value,
|
|
210
|
+
flags,
|
|
211
|
+
metadata,
|
|
212
|
+
category: dictionaryEntry?.category ?? entry.category,
|
|
213
|
+
tooltip: entry.tooltip ?? entry.helpText,
|
|
214
|
+
description: dictionaryEntry?.description ?? entry.description,
|
|
215
|
+
displayValue: this.formatDisplayValue(value, type),
|
|
216
|
+
dictionaryEntry,
|
|
217
|
+
isReadOnly: Boolean(entry.isReadOnly || entry.readOnly || flags?.some((f) => f.toLowerCase().includes('readonly')))
|
|
218
|
+
};
|
|
219
|
+
(propertyInfo as any).__filtered = filtered;
|
|
220
|
+
return propertyInfo;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private flattenPropertyMap(source: Record<string, any>, prefix = '', detailed = false): PropertyInfo[] {
|
|
224
|
+
const properties: PropertyInfo[] = [];
|
|
225
|
+
for (const [rawKey, rawValue] of Object.entries(source)) {
|
|
226
|
+
const name = prefix ? `${prefix}.${rawKey}` : rawKey;
|
|
227
|
+
if (this.isLikelyPropertyDescriptor(rawValue)) {
|
|
228
|
+
const normalized = this.normalizePropertyEntry({ ...rawValue, name }, detailed);
|
|
229
|
+
if (normalized) properties.push(normalized);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (this.isPlainObject(rawValue)) {
|
|
234
|
+
const nestedKeys = Object.keys(rawValue);
|
|
235
|
+
const hasPrimitiveChildren = nestedKeys.some((key) => {
|
|
236
|
+
const child = rawValue[key];
|
|
237
|
+
return child === null || typeof child !== 'object' || Array.isArray(child) || this.isLikelyPropertyDescriptor(child);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
if (hasPrimitiveChildren) {
|
|
241
|
+
const normalized = this.normalizePropertyEntry({ name, value: rawValue }, detailed);
|
|
242
|
+
if (normalized) properties.push(normalized);
|
|
243
|
+
} else {
|
|
244
|
+
properties.push(...this.flattenPropertyMap(rawValue, name, detailed));
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const normalized = this.normalizePropertyEntry({ name, value: rawValue }, detailed);
|
|
250
|
+
if (normalized) properties.push(normalized);
|
|
251
|
+
}
|
|
252
|
+
return properties;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private extractRawProperties(rawInfo: any, detailed = false): PropertyInfo[] {
|
|
256
|
+
if (!rawInfo) return [];
|
|
257
|
+
if (Array.isArray(rawInfo.properties)) {
|
|
258
|
+
const entries = rawInfo.properties as Array<Record<string, unknown>>;
|
|
259
|
+
return entries
|
|
260
|
+
.map((entry) => this.normalizePropertyEntry(entry, detailed))
|
|
261
|
+
.filter((entry): entry is PropertyInfo => Boolean(entry));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (this.isPlainObject(rawInfo.properties)) {
|
|
265
|
+
return this.flattenPropertyMap(rawInfo.properties, '', detailed);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (Array.isArray(rawInfo)) {
|
|
269
|
+
const entries = rawInfo as Array<Record<string, unknown>>;
|
|
270
|
+
return entries
|
|
271
|
+
.map((entry) => this.normalizePropertyEntry(entry, detailed))
|
|
272
|
+
.filter((entry): entry is PropertyInfo => Boolean(entry));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.isPlainObject(rawInfo)) {
|
|
276
|
+
const shallow = { ...rawInfo };
|
|
277
|
+
delete shallow.properties;
|
|
278
|
+
delete shallow.functions;
|
|
279
|
+
delete shallow.summary;
|
|
280
|
+
return this.flattenPropertyMap(shallow, '', detailed);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
curateObjectInfo(rawInfo: any, objectPath: string, detailed = false): ObjectInfo {
|
|
287
|
+
const properties = this.extractRawProperties(rawInfo, detailed);
|
|
288
|
+
const filteredProperties: string[] = [];
|
|
289
|
+
const curatedProperties = properties.filter((prop) => {
|
|
290
|
+
const shouldFilter = (prop as any).__filtered;
|
|
291
|
+
delete (prop as any).__filtered;
|
|
292
|
+
if (shouldFilter) {
|
|
293
|
+
filteredProperties.push(prop.name);
|
|
294
|
+
}
|
|
295
|
+
return !shouldFilter;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const categories: Record<string, number> = {};
|
|
299
|
+
const finalList = (detailed ? properties : curatedProperties).map((prop) => {
|
|
300
|
+
const category = (prop.category ?? prop.dictionaryEntry?.category ?? 'General');
|
|
301
|
+
categories[category] = (categories[category] ?? 0) + 1;
|
|
302
|
+
return prop;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const summary: ObjectSummary = {
|
|
306
|
+
name: rawInfo?.name ?? rawInfo?.objectName ?? rawInfo?.displayName,
|
|
307
|
+
class: rawInfo?.class ?? rawInfo?.className ?? rawInfo?.type ?? rawInfo?.objectClass,
|
|
308
|
+
path: rawInfo?.path ?? rawInfo?.objectPath ?? objectPath,
|
|
309
|
+
parent: rawInfo?.outer ?? rawInfo?.parent,
|
|
310
|
+
tags: Array.isArray(rawInfo?.tags) ? rawInfo.tags : undefined,
|
|
311
|
+
propertyCount: properties.length,
|
|
312
|
+
curatedPropertyCount: curatedProperties.length,
|
|
313
|
+
filteredPropertyCount: filteredProperties.length,
|
|
314
|
+
categories
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const info: ObjectInfo = {
|
|
318
|
+
class: summary.class,
|
|
319
|
+
name: summary.name,
|
|
320
|
+
path: summary.path,
|
|
321
|
+
parent: summary.parent,
|
|
322
|
+
tags: summary.tags,
|
|
323
|
+
properties: finalList,
|
|
324
|
+
functions: Array.isArray(rawInfo?.functions) ? rawInfo.functions : undefined,
|
|
325
|
+
interfaces: Array.isArray(rawInfo?.interfaces) ? rawInfo.interfaces : undefined,
|
|
326
|
+
flags: Array.isArray(rawInfo?.flags) ? rawInfo.flags : undefined,
|
|
327
|
+
summary,
|
|
328
|
+
filteredProperties: filteredProperties.length ? filteredProperties : undefined,
|
|
329
|
+
original: rawInfo
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
return info;
|
|
333
|
+
}
|
|
334
|
+
|
|
137
335
|
async inspectObject(params: { objectPath: string; detailed?: boolean }) {
|
|
138
336
|
// Check cache first if not requesting detailed info
|
|
139
337
|
if (!params.detailed && this.objectCache.has(params.objectPath)) {
|
|
@@ -142,256 +340,60 @@ export class IntrospectionTools {
|
|
|
142
340
|
return { success: true, info: cached };
|
|
143
341
|
}
|
|
144
342
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
path = r"${params.objectPath}"
|
|
149
|
-
detailed = ${params.detailed ? 'True' : 'False'}
|
|
150
|
-
|
|
151
|
-
def get_property_info(prop, obj=None):
|
|
152
|
-
"""Extract detailed property information"""
|
|
153
|
-
try:
|
|
154
|
-
info = {
|
|
155
|
-
'name': prop.get_name(),
|
|
156
|
-
'type': prop.get_property_class_name() if hasattr(prop, 'get_property_class_name') else 'Unknown'
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
# Try to get property flags
|
|
160
|
-
flags = []
|
|
161
|
-
if hasattr(prop, 'has_any_property_flags'):
|
|
162
|
-
if prop.has_any_property_flags(unreal.PropertyFlags.CPF_EDIT_CONST):
|
|
163
|
-
flags.append('ReadOnly')
|
|
164
|
-
if prop.has_any_property_flags(unreal.PropertyFlags.CPF_BLUEPRINT_READ_ONLY):
|
|
165
|
-
flags.append('BlueprintReadOnly')
|
|
166
|
-
if prop.has_any_property_flags(unreal.PropertyFlags.CPF_TRANSIENT):
|
|
167
|
-
flags.append('Transient')
|
|
168
|
-
info['flags'] = flags
|
|
169
|
-
|
|
170
|
-
# Try to get metadata
|
|
171
|
-
if hasattr(prop, 'get_metadata'):
|
|
172
|
-
try:
|
|
173
|
-
info['category'] = prop.get_metadata('Category')
|
|
174
|
-
info['tooltip'] = prop.get_metadata('ToolTip')
|
|
175
|
-
except Exception:
|
|
176
|
-
pass
|
|
177
|
-
|
|
178
|
-
# Try to get current value if object provided
|
|
179
|
-
if obj and detailed:
|
|
180
|
-
try:
|
|
181
|
-
value = getattr(obj, prop.get_name())
|
|
182
|
-
# Convert complex types to serializable format
|
|
183
|
-
if hasattr(value, '__dict__'):
|
|
184
|
-
value = str(value)
|
|
185
|
-
info['value'] = value
|
|
186
|
-
except Exception:
|
|
187
|
-
pass
|
|
188
|
-
|
|
189
|
-
return info
|
|
190
|
-
except Exception as e:
|
|
191
|
-
return {'name': str(prop) if prop else 'Unknown', 'type': 'Unknown', 'error': str(e)}
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
obj = unreal.load_object(None, path)
|
|
195
|
-
if not obj:
|
|
196
|
-
# Try as class if object load fails
|
|
197
|
-
try:
|
|
198
|
-
obj = unreal.load_class(None, path)
|
|
199
|
-
if not obj:
|
|
200
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Object or class not found'}))
|
|
201
|
-
raise SystemExit(0)
|
|
202
|
-
except Exception:
|
|
203
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Object not found'}))
|
|
204
|
-
raise SystemExit(0)
|
|
205
|
-
|
|
206
|
-
info = {
|
|
207
|
-
'class': obj.get_class().get_name() if hasattr(obj, 'get_class') else str(type(obj)),
|
|
208
|
-
'name': obj.get_name() if hasattr(obj, 'get_name') else '',
|
|
209
|
-
'path': path,
|
|
210
|
-
'properties': [],
|
|
211
|
-
'functions': [],
|
|
212
|
-
'flags': []
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
# Get parent class
|
|
216
|
-
try:
|
|
217
|
-
if hasattr(obj, 'get_class'):
|
|
218
|
-
cls = obj.get_class()
|
|
219
|
-
if hasattr(cls, 'get_super_class'):
|
|
220
|
-
super_cls = cls.get_super_class()
|
|
221
|
-
if super_cls:
|
|
222
|
-
info['parent'] = super_cls.get_name()
|
|
223
|
-
except Exception:
|
|
224
|
-
pass
|
|
225
|
-
|
|
226
|
-
# Get object flags
|
|
227
|
-
try:
|
|
228
|
-
if hasattr(obj, 'has_any_flags'):
|
|
229
|
-
flags = []
|
|
230
|
-
if obj.has_any_flags(unreal.ObjectFlags.RF_PUBLIC):
|
|
231
|
-
flags.append('Public')
|
|
232
|
-
if obj.has_any_flags(unreal.ObjectFlags.RF_TRANSIENT):
|
|
233
|
-
flags.append('Transient')
|
|
234
|
-
if obj.has_any_flags(unreal.ObjectFlags.RF_DEFAULT_SUB_OBJECT):
|
|
235
|
-
flags.append('DefaultSubObject')
|
|
236
|
-
info['flags'] = flags
|
|
237
|
-
except Exception:
|
|
238
|
-
pass
|
|
239
|
-
|
|
240
|
-
# Get properties - AVOID deprecated properties completely
|
|
241
|
-
props = []
|
|
242
|
-
|
|
243
|
-
# List of deprecated properties to completely skip
|
|
244
|
-
deprecated_props = [
|
|
245
|
-
'life_span', 'on_actor_touch', 'on_actor_un_touch',
|
|
246
|
-
'get_touching_actors', 'get_touching_components',
|
|
247
|
-
'controller_class', 'look_up_scale', 'sound_wave_param',
|
|
248
|
-
'material_substitute', 'texture_object'
|
|
249
|
-
]
|
|
250
|
-
|
|
251
|
-
try:
|
|
252
|
-
cls = obj.get_class() if hasattr(obj, 'get_class') else obj
|
|
253
|
-
|
|
254
|
-
# Try UE5 reflection API with safe property access
|
|
255
|
-
if hasattr(cls, 'get_properties'):
|
|
256
|
-
for prop in cls.get_properties():
|
|
257
|
-
prop_name = None
|
|
258
|
-
try:
|
|
259
|
-
# Safe property name extraction
|
|
260
|
-
if hasattr(prop, 'get_name'):
|
|
261
|
-
prop_name = prop.get_name()
|
|
262
|
-
elif hasattr(prop, 'name'):
|
|
263
|
-
prop_name = prop.name
|
|
264
|
-
|
|
265
|
-
# Skip if deprecated
|
|
266
|
-
if prop_name and prop_name not in deprecated_props:
|
|
267
|
-
prop_info = get_property_info(prop, obj if detailed else None)
|
|
268
|
-
if prop_info.get('name') not in deprecated_props:
|
|
269
|
-
props.append(prop_info)
|
|
270
|
-
except Exception:
|
|
271
|
-
pass
|
|
272
|
-
|
|
273
|
-
# If reflection API didn't work, use a safe property list
|
|
274
|
-
if not props:
|
|
275
|
-
# Only access known safe properties
|
|
276
|
-
safe_properties = [
|
|
277
|
-
'actor_guid', 'actor_instance_guid', 'always_relevant',
|
|
278
|
-
'auto_destroy_when_finished', 'can_be_damaged',
|
|
279
|
-
'content_bundle_guid', 'custom_time_dilation',
|
|
280
|
-
'enable_auto_lod_generation', 'find_camera_component_when_view_target',
|
|
281
|
-
'generate_overlap_events_during_level_streaming', 'hidden',
|
|
282
|
-
'initial_life_span', # Use new name instead of life_span
|
|
283
|
-
'instigator', 'is_spatially_loaded', 'min_net_update_frequency',
|
|
284
|
-
'net_cull_distance_squared', 'net_dormancy', 'net_priority',
|
|
285
|
-
'net_update_frequency', 'net_use_owner_relevancy',
|
|
286
|
-
'only_relevant_to_owner', 'pivot_offset',
|
|
287
|
-
'replicate_using_registered_sub_object_list', 'replicates',
|
|
288
|
-
'root_component', 'runtime_grid', 'spawn_collision_handling_method',
|
|
289
|
-
'sprite_scale', 'tags', 'location', 'rotation', 'scale'
|
|
290
|
-
]
|
|
291
|
-
|
|
292
|
-
for prop_name in safe_properties:
|
|
293
|
-
try:
|
|
294
|
-
# Use get_editor_property for safer access
|
|
295
|
-
if hasattr(obj, 'get_editor_property'):
|
|
296
|
-
val = obj.get_editor_property(prop_name)
|
|
297
|
-
props.append({
|
|
298
|
-
'name': prop_name,
|
|
299
|
-
'type': type(val).__name__ if val is not None else 'None',
|
|
300
|
-
'value': str(val)[:100] if detailed and val is not None else None
|
|
301
|
-
})
|
|
302
|
-
elif hasattr(obj, prop_name):
|
|
303
|
-
# Direct access only for safe properties
|
|
304
|
-
val = getattr(obj, prop_name)
|
|
305
|
-
if not callable(val):
|
|
306
|
-
props.append({
|
|
307
|
-
'name': prop_name,
|
|
308
|
-
'type': type(val).__name__,
|
|
309
|
-
'value': str(val)[:100] if detailed else None
|
|
310
|
-
})
|
|
311
|
-
except Exception:
|
|
312
|
-
pass
|
|
313
|
-
except Exception as e:
|
|
314
|
-
# Minimal fallback with only essential safe properties
|
|
315
|
-
pass
|
|
316
|
-
|
|
317
|
-
info['properties'] = props
|
|
318
|
-
|
|
319
|
-
# Get functions/methods if detailed
|
|
320
|
-
if detailed:
|
|
321
|
-
funcs = []
|
|
322
|
-
try:
|
|
323
|
-
cls = obj.get_class() if hasattr(obj, 'get_class') else obj
|
|
324
|
-
|
|
325
|
-
# Try to get UFunctions
|
|
326
|
-
if hasattr(cls, 'get_functions'):
|
|
327
|
-
for func in cls.get_functions():
|
|
328
|
-
func_info = {
|
|
329
|
-
'name': func.get_name(),
|
|
330
|
-
'parameters': [],
|
|
331
|
-
'flags': []
|
|
332
|
-
}
|
|
333
|
-
# Get parameters if possible
|
|
334
|
-
if hasattr(func, 'get_params'):
|
|
335
|
-
for param in func.get_params():
|
|
336
|
-
func_info['parameters'].append({
|
|
337
|
-
'name': param.get_name() if hasattr(param, 'get_name') else str(param),
|
|
338
|
-
'type': 'Unknown'
|
|
339
|
-
})
|
|
340
|
-
funcs.append(func_info)
|
|
341
|
-
else:
|
|
342
|
-
# Fallback: use known safe function names
|
|
343
|
-
safe_functions = [
|
|
344
|
-
'get_actor_location', 'set_actor_location',
|
|
345
|
-
'get_actor_rotation', 'set_actor_rotation',
|
|
346
|
-
'get_actor_scale', 'set_actor_scale',
|
|
347
|
-
'destroy_actor', 'destroy_component',
|
|
348
|
-
'get_components', 'get_component_by_class',
|
|
349
|
-
'add_actor_component', 'add_component',
|
|
350
|
-
'get_world', 'get_name', 'get_path_name',
|
|
351
|
-
'is_valid', 'is_a', 'has_authority'
|
|
352
|
-
]
|
|
353
|
-
for func_name in safe_functions:
|
|
354
|
-
if hasattr(obj, func_name):
|
|
355
|
-
try:
|
|
356
|
-
attr_value = getattr(obj, func_name)
|
|
357
|
-
if callable(attr_value):
|
|
358
|
-
funcs.append({
|
|
359
|
-
'name': func_name,
|
|
360
|
-
'parameters': [],
|
|
361
|
-
'flags': []
|
|
362
|
-
})
|
|
363
|
-
except Exception:
|
|
364
|
-
pass
|
|
365
|
-
except Exception:
|
|
366
|
-
pass
|
|
367
|
-
|
|
368
|
-
info['functions'] = funcs
|
|
369
|
-
|
|
370
|
-
print('RESULT:' + json.dumps({'success': True, 'info': info}))
|
|
371
|
-
except Exception as e:
|
|
372
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
373
|
-
`.trim();
|
|
374
|
-
const resp = await this.executeWithRetry(
|
|
375
|
-
() => this.bridge.executePython(py),
|
|
376
|
-
'inspectObject'
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
const result = this.parsePythonResult(resp, 'inspectObject');
|
|
380
|
-
|
|
381
|
-
// Cache the result if successful and not detailed
|
|
382
|
-
if (result.success && result.info && !params.detailed) {
|
|
383
|
-
this.objectCache.set(params.objectPath, result.info);
|
|
343
|
+
|
|
344
|
+
if (!this.automationBridge) {
|
|
345
|
+
throw new Error('Automation Bridge not available. Introspection operations require plugin support.');
|
|
384
346
|
}
|
|
385
|
-
|
|
386
|
-
|
|
347
|
+
|
|
348
|
+
const automationBridge = this.automationBridge;
|
|
349
|
+
return this.executeWithRetry(async () => {
|
|
350
|
+
try {
|
|
351
|
+
const response = await automationBridge.sendAutomationRequest('inspect_object', {
|
|
352
|
+
objectPath: params.objectPath,
|
|
353
|
+
detailed: params.detailed ?? false
|
|
354
|
+
}, {
|
|
355
|
+
timeoutMs: 60000
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (response.success === false) {
|
|
359
|
+
return {
|
|
360
|
+
success: false,
|
|
361
|
+
error: response.error || response.message || 'Failed to inspect object'
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const rawInfo = response.info ?? response.result ?? response.data ?? response;
|
|
366
|
+
const curatedInfo = this.curateObjectInfo(rawInfo, params.objectPath, params.detailed ?? false);
|
|
367
|
+
|
|
368
|
+
const result = {
|
|
369
|
+
success: true,
|
|
370
|
+
info: curatedInfo
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// Cache the result if successful and not detailed
|
|
374
|
+
if (result.success && result.info && !params.detailed) {
|
|
375
|
+
this.objectCache.set(params.objectPath, result.info as ObjectInfo);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return result;
|
|
379
|
+
} catch (error) {
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
error: `Failed to inspect object: ${error instanceof Error ? error.message : String(error)}`
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}, 'inspectObject');
|
|
387
386
|
}
|
|
388
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Set property value on an object
|
|
390
|
+
*/
|
|
389
391
|
async setProperty(params: { objectPath: string; propertyName: string; value: any }) {
|
|
390
392
|
return this.executeWithRetry(async () => {
|
|
391
393
|
try {
|
|
392
394
|
// Validate and convert value type if needed
|
|
393
395
|
let processedValue = params.value;
|
|
394
|
-
|
|
396
|
+
|
|
395
397
|
// Handle special Unreal types
|
|
396
398
|
if (typeof params.value === 'object' && params.value !== null) {
|
|
397
399
|
// Vector conversion
|
|
@@ -422,23 +424,23 @@ except Exception as e:
|
|
|
422
424
|
'Rotator'
|
|
423
425
|
),
|
|
424
426
|
Scale3D: this.convertPropertyValue(
|
|
425
|
-
params.value.scale || params.value.Scale || {x: 1, y: 1, z: 1},
|
|
427
|
+
params.value.scale || params.value.Scale || { x: 1, y: 1, z: 1 },
|
|
426
428
|
'Vector'
|
|
427
429
|
)
|
|
428
430
|
};
|
|
429
431
|
}
|
|
430
432
|
}
|
|
431
|
-
|
|
432
|
-
const res = await this.bridge.httpCall('/remote/object/property', 'PUT', {
|
|
433
|
+
const res = await this.bridge.setObjectProperty({
|
|
433
434
|
objectPath: params.objectPath,
|
|
434
435
|
propertyName: params.propertyName,
|
|
435
|
-
|
|
436
|
+
value: processedValue
|
|
436
437
|
});
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
438
|
+
|
|
439
|
+
if (res.success) {
|
|
440
|
+
this.objectCache.delete(params.objectPath);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return res;
|
|
442
444
|
} catch (err: any) {
|
|
443
445
|
const errorMsg = err?.message || String(err);
|
|
444
446
|
if (errorMsg.includes('404')) {
|
|
@@ -456,77 +458,17 @@ except Exception as e:
|
|
|
456
458
|
* Get property value of an object
|
|
457
459
|
*/
|
|
458
460
|
async getProperty(params: { objectPath: string; propertyName: string }) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
# Try different methods to get property
|
|
469
|
-
value = None
|
|
470
|
-
found = False
|
|
471
|
-
|
|
472
|
-
# Method 1: Direct attribute access
|
|
473
|
-
if hasattr(obj, prop_name):
|
|
474
|
-
try:
|
|
475
|
-
value = getattr(obj, prop_name)
|
|
476
|
-
found = True
|
|
477
|
-
except Exception:
|
|
478
|
-
pass
|
|
479
|
-
|
|
480
|
-
# Method 2: get_editor_property (UE4/5)
|
|
481
|
-
if not found and hasattr(obj, 'get_editor_property'):
|
|
482
|
-
try:
|
|
483
|
-
value = obj.get_editor_property(prop_name)
|
|
484
|
-
found = True
|
|
485
|
-
except Exception:
|
|
486
|
-
pass
|
|
487
|
-
|
|
488
|
-
# Method 3: Try with common property name variations
|
|
489
|
-
if not found:
|
|
490
|
-
# Try common property name variations
|
|
491
|
-
variations = [
|
|
492
|
-
prop_name,
|
|
493
|
-
prop_name.lower(),
|
|
494
|
-
prop_name.upper(),
|
|
495
|
-
prop_name.capitalize(),
|
|
496
|
-
# Convert snake_case to CamelCase
|
|
497
|
-
''.join(word.capitalize() for word in prop_name.split('_')),
|
|
498
|
-
# Convert CamelCase to snake_case
|
|
499
|
-
''.join(['_' + c.lower() if c.isupper() else c for c in prop_name]).lstrip('_')
|
|
500
|
-
]
|
|
501
|
-
for variant in variations:
|
|
502
|
-
if hasattr(obj, variant):
|
|
503
|
-
try:
|
|
504
|
-
value = getattr(obj, variant)
|
|
505
|
-
found = True
|
|
506
|
-
break
|
|
507
|
-
except Exception:
|
|
508
|
-
pass
|
|
509
|
-
|
|
510
|
-
if found:
|
|
511
|
-
# Convert complex types to string
|
|
512
|
-
if hasattr(value, '__dict__'):
|
|
513
|
-
value = str(value)
|
|
514
|
-
elif isinstance(value, (list, tuple, dict)):
|
|
515
|
-
value = json.dumps(value)
|
|
516
|
-
|
|
517
|
-
print('RESULT:' + json.dumps({'success': True, 'value': value}))
|
|
518
|
-
else:
|
|
519
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Property {prop_name} not found'}))
|
|
520
|
-
except Exception as e:
|
|
521
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
522
|
-
`.trim();
|
|
523
|
-
|
|
524
|
-
const resp = await this.executeWithRetry(
|
|
525
|
-
() => this.bridge.executePython(py),
|
|
461
|
+
return this.executeWithRetry(
|
|
462
|
+
async () => {
|
|
463
|
+
const result = await this.bridge.getObjectProperty({
|
|
464
|
+
objectPath: params.objectPath,
|
|
465
|
+
propertyName: params.propertyName
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
return result;
|
|
469
|
+
},
|
|
526
470
|
'getProperty'
|
|
527
471
|
);
|
|
528
|
-
|
|
529
|
-
return this.parsePythonResult(resp, 'getProperty');
|
|
530
472
|
}
|
|
531
473
|
|
|
532
474
|
/**
|
|
@@ -537,238 +479,205 @@ except Exception as e:
|
|
|
537
479
|
functionName: string;
|
|
538
480
|
parameters?: any[];
|
|
539
481
|
}) {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if callable(func):
|
|
574
|
-
result = func(*params) if params else func()
|
|
575
|
-
if hasattr(result, '__dict__'):
|
|
576
|
-
result = str(result)
|
|
577
|
-
print('RESULT:' + json.dumps({'success': True, 'result': result}))
|
|
578
|
-
else:
|
|
579
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'{func_name} is not callable'}))
|
|
580
|
-
else:
|
|
581
|
-
# Try snake_case version
|
|
582
|
-
snake_case_name = ''.join(['_' + c.lower() if c.isupper() else c for c in func_name]).lstrip('_')
|
|
583
|
-
if hasattr(obj, snake_case_name):
|
|
584
|
-
func = getattr(obj, snake_case_name)
|
|
585
|
-
result = func(*params) if params else func()
|
|
586
|
-
print('RESULT:' + json.dumps({'success': True, 'result': result}))
|
|
587
|
-
else:
|
|
588
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Function {func_name} not found'}))
|
|
589
|
-
except Exception as e:
|
|
590
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Function call failed: {str(e)}'}))
|
|
591
|
-
else:
|
|
592
|
-
# Regular object method call
|
|
593
|
-
if hasattr(obj, func_name):
|
|
594
|
-
func = getattr(obj, func_name)
|
|
595
|
-
if callable(func):
|
|
596
|
-
try:
|
|
597
|
-
result = func(*params) if params else func()
|
|
598
|
-
# Convert result to serializable format
|
|
599
|
-
if hasattr(result, '__dict__'):
|
|
600
|
-
result = str(result)
|
|
601
|
-
print('RESULT:' + json.dumps({'success': True, 'result': result}))
|
|
602
|
-
except Exception as e:
|
|
603
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Function call failed: {str(e)}'}))
|
|
604
|
-
else:
|
|
605
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'{func_name} is not callable'}))
|
|
606
|
-
else:
|
|
607
|
-
print('RESULT:' + json.dumps({'success': False, 'error': f'Function {func_name} not found'}))
|
|
608
|
-
except Exception as e:
|
|
609
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
610
|
-
`.trim();
|
|
611
|
-
|
|
612
|
-
const resp = await this.executeWithRetry(
|
|
613
|
-
() => this.bridge.executePython(py),
|
|
614
|
-
'callFunction'
|
|
615
|
-
);
|
|
616
|
-
|
|
617
|
-
return this.parsePythonResult(resp, 'callFunction');
|
|
482
|
+
if (!this.automationBridge) {
|
|
483
|
+
throw new Error('Automation Bridge not available. Function call operations require plugin support.');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const automationBridge = this.automationBridge;
|
|
487
|
+
return this.executeWithRetry(async () => {
|
|
488
|
+
try {
|
|
489
|
+
const response = await automationBridge.sendAutomationRequest('call_object_function', {
|
|
490
|
+
objectPath: params.objectPath,
|
|
491
|
+
functionName: params.functionName,
|
|
492
|
+
parameters: params.parameters || []
|
|
493
|
+
}, {
|
|
494
|
+
timeoutMs: 60000
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
if (response.success === false) {
|
|
498
|
+
return {
|
|
499
|
+
success: false,
|
|
500
|
+
error: response.error || response.message || 'Failed to call function'
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
success: true,
|
|
506
|
+
result: response.result
|
|
507
|
+
};
|
|
508
|
+
} catch (error) {
|
|
509
|
+
return {
|
|
510
|
+
success: false,
|
|
511
|
+
error: `Failed to call function: ${error instanceof Error ? error.message : String(error)}`
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}, 'callFunction');
|
|
618
515
|
}
|
|
619
516
|
|
|
620
517
|
/**
|
|
621
518
|
* Get Class Default Object (CDO) for a class
|
|
622
519
|
*/
|
|
623
520
|
async getCDO(className: string) {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if cdo:
|
|
660
|
-
info = {
|
|
661
|
-
'className': cls.get_name(),
|
|
662
|
-
'cdoPath': cdo.get_path_name() if hasattr(cdo, 'get_path_name') else '',
|
|
663
|
-
'properties': []
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
# Get default property values using safe property list
|
|
667
|
-
safe_cdo_properties = [
|
|
668
|
-
'initial_life_span', 'hidden', 'can_be_damaged', 'replicates',
|
|
669
|
-
'always_relevant', 'net_dormancy', 'net_priority',
|
|
670
|
-
'net_update_frequency', 'replicate_movement',
|
|
671
|
-
'actor_guid', 'tags', 'root_component',
|
|
672
|
-
'auto_destroy_when_finished', 'enable_auto_lod_generation'
|
|
673
|
-
]
|
|
674
|
-
for prop_name in safe_cdo_properties:
|
|
675
|
-
try:
|
|
676
|
-
if hasattr(cdo, 'get_editor_property'):
|
|
677
|
-
value = cdo.get_editor_property(prop_name)
|
|
678
|
-
info['properties'].append({
|
|
679
|
-
'name': prop_name,
|
|
680
|
-
'defaultValue': str(value)[:100]
|
|
681
|
-
})
|
|
682
|
-
elif hasattr(cdo, prop_name):
|
|
683
|
-
value = getattr(cdo, prop_name)
|
|
684
|
-
if not callable(value):
|
|
685
|
-
info['properties'].append({
|
|
686
|
-
'name': prop_name,
|
|
687
|
-
'defaultValue': str(value)[:100]
|
|
688
|
-
})
|
|
689
|
-
except Exception:
|
|
690
|
-
pass
|
|
691
|
-
|
|
692
|
-
print('RESULT:' + json.dumps({'success': True, 'cdo': info}))
|
|
693
|
-
else:
|
|
694
|
-
print('RESULT:' + json.dumps({'success': False, 'error': 'Could not get CDO'}))
|
|
695
|
-
except Exception as e:
|
|
696
|
-
print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
|
|
697
|
-
`.trim();
|
|
698
|
-
|
|
699
|
-
const resp = await this.executeWithRetry(
|
|
700
|
-
() => this.bridge.executePython(py),
|
|
701
|
-
'getCDO'
|
|
702
|
-
);
|
|
703
|
-
|
|
704
|
-
return this.parsePythonResult(resp, 'getCDO');
|
|
521
|
+
if (!this.automationBridge) {
|
|
522
|
+
throw new Error('Automation Bridge not available. CDO operations require plugin support.');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const automationBridge = this.automationBridge;
|
|
526
|
+
return this.executeWithRetry(async () => {
|
|
527
|
+
try {
|
|
528
|
+
const response: any = await automationBridge.sendAutomationRequest('inspect', {
|
|
529
|
+
action: 'inspect_class',
|
|
530
|
+
// C++ plugin expects `classPath` for inspect_class, but accepts both
|
|
531
|
+
// short names and full paths (e.g. "Actor" or "/Script/Engine.Actor").
|
|
532
|
+
classPath: className
|
|
533
|
+
}, {
|
|
534
|
+
timeoutMs: 60000
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
if (response?.success === false) {
|
|
538
|
+
return {
|
|
539
|
+
success: false,
|
|
540
|
+
error: response.error || response.message || 'Failed to get CDO'
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
success: true,
|
|
546
|
+
// Plugin returns class inspection data under data/result depending on bridge version.
|
|
547
|
+
cdo: response?.data ?? response?.result ?? response
|
|
548
|
+
};
|
|
549
|
+
} catch (error) {
|
|
550
|
+
return {
|
|
551
|
+
success: false,
|
|
552
|
+
error: `Failed to get CDO: ${error instanceof Error ? error.message : String(error)}`
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
}, 'getCDO');
|
|
705
556
|
}
|
|
706
557
|
|
|
707
558
|
/**
|
|
708
559
|
* Search for objects by class
|
|
709
560
|
*/
|
|
710
561
|
async findObjectsByClass(className: string, limit: number = 100) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
562
|
+
if (!this.automationBridge) {
|
|
563
|
+
throw new Error('Automation Bridge not available. Object search operations require plugin support.');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const automationBridge = this.automationBridge;
|
|
567
|
+
return this.executeWithRetry(async () => {
|
|
568
|
+
try {
|
|
569
|
+
const response: any = await automationBridge.sendAutomationRequest('inspect', {
|
|
570
|
+
action: 'find_by_class',
|
|
571
|
+
className,
|
|
572
|
+
limit
|
|
573
|
+
}, {
|
|
574
|
+
timeoutMs: 60000
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
if (response?.success === false) {
|
|
578
|
+
return {
|
|
579
|
+
success: false,
|
|
580
|
+
error: response.error || response.message || 'Failed to find objects'
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const data = response?.data ?? response?.result ?? response;
|
|
585
|
+
const validObjects = Array.isArray(data?.actors)
|
|
586
|
+
? data.actors
|
|
587
|
+
: (Array.isArray(data?.objects) ? data.objects : (Array.isArray(data) ? data : []));
|
|
588
|
+
return {
|
|
589
|
+
success: true,
|
|
590
|
+
message: `Found ${validObjects.length} objects`,
|
|
591
|
+
objects: validObjects,
|
|
592
|
+
count: validObjects.length
|
|
593
|
+
};
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return {
|
|
596
|
+
success: false,
|
|
597
|
+
error: `Failed to find objects: ${error instanceof Error ? error.message : String(error)}`
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}, 'findObjectsByClass');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Get property value of a component
|
|
605
|
+
*/
|
|
606
|
+
async getComponentProperty(params: { objectPath: string; componentName: string; propertyName: string }) {
|
|
607
|
+
if (!this.automationBridge) {
|
|
608
|
+
throw new Error('Automation Bridge not available. Component property operations require plugin support.');
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const automationBridge = this.automationBridge;
|
|
612
|
+
return this.executeWithRetry(async () => {
|
|
613
|
+
try {
|
|
614
|
+
const response = await automationBridge.sendAutomationRequest('get_component_property', {
|
|
615
|
+
objectPath: params.objectPath,
|
|
616
|
+
componentName: params.componentName,
|
|
617
|
+
propertyName: params.propertyName
|
|
618
|
+
}, {
|
|
619
|
+
timeoutMs: 15000
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
if (response.success === false) {
|
|
623
|
+
return {
|
|
624
|
+
success: false,
|
|
625
|
+
error: response.error || response.message || 'Failed to get component property'
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
success: true,
|
|
631
|
+
value: response.value,
|
|
632
|
+
type: response.type
|
|
633
|
+
};
|
|
634
|
+
} catch (error) {
|
|
635
|
+
return {
|
|
636
|
+
success: false,
|
|
637
|
+
error: `Failed to get component property: ${error instanceof Error ? error.message : String(error)}`
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}, 'getComponentProperty');
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Set property value of a component
|
|
645
|
+
*/
|
|
646
|
+
async setComponentProperty(params: { objectPath: string; componentName: string; propertyName: string; value: any }) {
|
|
647
|
+
if (!this.automationBridge) {
|
|
648
|
+
throw new Error('Automation Bridge not available. Component property operations require plugin support.');
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const automationBridge = this.automationBridge;
|
|
652
|
+
return this.executeWithRetry(async () => {
|
|
653
|
+
try {
|
|
654
|
+
const response = await automationBridge.sendAutomationRequest('set_component_property', {
|
|
655
|
+
objectPath: params.objectPath,
|
|
656
|
+
componentName: params.componentName,
|
|
657
|
+
propertyName: params.propertyName,
|
|
658
|
+
value: params.value
|
|
659
|
+
}, {
|
|
660
|
+
timeoutMs: 15000
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
if (response.success === false) {
|
|
664
|
+
return {
|
|
665
|
+
success: false,
|
|
666
|
+
error: response.error || response.message || 'Failed to set component property'
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return {
|
|
671
|
+
success: true,
|
|
672
|
+
message: response.message || 'Property set successfully'
|
|
673
|
+
};
|
|
674
|
+
} catch (error) {
|
|
675
|
+
return {
|
|
676
|
+
success: false,
|
|
677
|
+
error: `Failed to set component property: ${error instanceof Error ? error.message : String(error)}`
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
}, 'setComponentProperty');
|
|
772
681
|
}
|
|
773
682
|
|
|
774
683
|
/**
|