ue-mcp 1.2.3 → 1.3.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/README.md +4 -3
- package/dist/action-class.js +56 -3
- package/dist/action-class.js.map +1 -1
- package/dist/bridge-timeouts.d.ts +64 -0
- package/dist/bridge-timeouts.js +118 -0
- package/dist/bridge-timeouts.js.map +1 -0
- package/dist/bridge.js +10 -2
- package/dist/bridge.js.map +1 -1
- package/dist/editor-control.d.ts +72 -5
- package/dist/editor-control.js +164 -140
- package/dist/editor-control.js.map +1 -1
- package/dist/editor-target.d.ts +55 -1
- package/dist/editor-target.js +107 -10
- package/dist/editor-target.js.map +1 -1
- package/dist/engine-observer.d.ts +59 -5
- package/dist/engine-observer.js +249 -29
- package/dist/engine-observer.js.map +1 -1
- package/dist/engine-root.d.ts +91 -0
- package/dist/engine-root.js +448 -0
- package/dist/engine-root.js.map +1 -0
- package/dist/epic-enrich.js +16 -1
- package/dist/epic-enrich.js.map +1 -1
- package/dist/feedback-cli.js +4 -0
- package/dist/feedback-cli.js.map +1 -1
- package/dist/feedback-deferred.d.ts +9 -0
- package/dist/feedback-deferred.js +2 -0
- package/dist/feedback-deferred.js.map +1 -1
- package/dist/feedback-fallback.d.ts +99 -0
- package/dist/feedback-fallback.js +173 -0
- package/dist/feedback-fallback.js.map +1 -0
- package/dist/flow/write-methods.js +20 -0
- package/dist/flow/write-methods.js.map +1 -1
- package/dist/index.js +41 -2
- package/dist/index.js.map +1 -1
- package/dist/lean-context.js +8 -3
- package/dist/lean-context.js.map +1 -1
- package/dist/locking.js +11 -2
- package/dist/locking.js.map +1 -1
- package/dist/plugin/loader.d.ts +4 -0
- package/dist/plugin/loader.js +22 -8
- package/dist/plugin/loader.js.map +1 -1
- package/dist/plugin/manifest.d.ts +72 -52
- package/dist/plugin/manifest.js +118 -21
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin-cli.js +16 -3
- package/dist/plugin-cli.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +23 -2
- package/dist/project.js.map +1 -1
- package/dist/python-gate.d.ts +95 -0
- package/dist/python-gate.js +175 -0
- package/dist/python-gate.js.map +1 -0
- package/dist/tool-counts.json +16 -16
- package/dist/tools/animation.js +288 -24
- package/dist/tools/animation.js.map +1 -1
- package/dist/tools/asset.js +79 -14
- package/dist/tools/asset.js.map +1 -1
- package/dist/tools/audio.js +59 -5
- package/dist/tools/audio.js.map +1 -1
- package/dist/tools/blueprint.js +29 -12
- package/dist/tools/blueprint.js.map +1 -1
- package/dist/tools/chooser.js +4 -1
- package/dist/tools/chooser.js.map +1 -1
- package/dist/tools/editor.js +81 -43
- package/dist/tools/editor.js.map +1 -1
- package/dist/tools/feedback.js +214 -48
- package/dist/tools/feedback.js.map +1 -1
- package/dist/tools/foliage.js +27 -1
- package/dist/tools/foliage.js.map +1 -1
- package/dist/tools/gameplay.js +38 -11
- package/dist/tools/gameplay.js.map +1 -1
- package/dist/tools/gas.js +17 -5
- package/dist/tools/gas.js.map +1 -1
- package/dist/tools/landscape.js +22 -7
- package/dist/tools/landscape.js.map +1 -1
- package/dist/tools/level.js +322 -51
- package/dist/tools/level.js.map +1 -1
- package/dist/tools/material.js +10 -4
- package/dist/tools/material.js.map +1 -1
- package/dist/tools/niagara.js +3 -2
- package/dist/tools/niagara.js.map +1 -1
- package/dist/tools/pcg.js +6 -5
- package/dist/tools/pcg.js.map +1 -1
- package/dist/tools/plugins.js +2 -0
- package/dist/tools/plugins.js.map +1 -1
- package/dist/tools/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.js +46 -3
- package/dist/types.js.map +1 -1
- package/dist/ue-mcp.default.yml +493 -125
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
- package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
- package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
- package/skills/ue-mcp-animation/SKILL.md +154 -0
- package/skills/ue-mcp-blueprint/SKILL.md +71 -0
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include "HandlerUtils.h"
|
|
10
10
|
|
|
11
11
|
#include "UObject/GCObjectScopeGuard.h"
|
|
12
|
+
#include "HandlerFunctionCall.h"
|
|
12
13
|
#include "HandlerJsonProperty.h"
|
|
13
14
|
#include "JsonSerializer.h"
|
|
14
15
|
#include "Containers/Ticker.h"
|
|
@@ -386,6 +387,12 @@ TSharedPtr<FJsonValue> FEditorHandlers::PieGetRuntimeValue(const TSharedPtr<FJso
|
|
|
386
387
|
return MCPError(TEXT("PIE is not active. Start a PIE session first."));
|
|
387
388
|
}
|
|
388
389
|
|
|
390
|
+
// This action shipped before #983 with 'actorPath' as a label|name|path
|
|
391
|
+
// token, not a strict path, and with 'actorLabel' as its fallback spelling.
|
|
392
|
+
// The shared resolver treats a path as precise, so the loose reading is
|
|
393
|
+
// kept here explicitly rather than silently dropped: a caller who has been
|
|
394
|
+
// passing a label in 'actorPath' for two releases must not start getting
|
|
395
|
+
// "no actor at that path".
|
|
389
396
|
FString ActorPath;
|
|
390
397
|
if (!Params->TryGetStringField(TEXT("actorPath"), ActorPath))
|
|
391
398
|
{
|
|
@@ -401,8 +408,25 @@ TSharedPtr<FJsonValue> FEditorHandlers::PieGetRuntimeValue(const TSharedPtr<FJso
|
|
|
401
408
|
|
|
402
409
|
// Search for the actor in the PIE world (accept label, name, or full path).
|
|
403
410
|
// #778: honour pieInstance so a client world is reachable.
|
|
411
|
+
// #983: through the shared resolver, so actorPath wins over actorLabel and
|
|
412
|
+
// a duplicated label is refused rather than read off one of the copies.
|
|
404
413
|
UWorld* PIEWorld = ResolveWorldFromParams(Params, TEXT("pie"));
|
|
405
|
-
|
|
414
|
+
FMCPActorSelector PieSel;
|
|
415
|
+
PieSel.Match = EMCPActorMatch::LabelNameOrPath;
|
|
416
|
+
PieSel.WorldLabel = TEXT("PIE");
|
|
417
|
+
TSharedPtr<FJsonValue> PieActorErr;
|
|
418
|
+
AActor* TargetActor = MCPResolveActor(PIEWorld, Params, PieActorErr, PieSel);
|
|
419
|
+
if (!TargetActor && MCPIsAmbiguousActorError(PieActorErr)) return PieActorErr;
|
|
420
|
+
|
|
421
|
+
// The legacy tolerance: retry the value of 'actorPath' as a label / name
|
|
422
|
+
// token. Ambiguity is still refused, so the loose reading cannot bring the
|
|
423
|
+
// silent wrong pick back with it.
|
|
424
|
+
if (!TargetActor && !ActorPath.IsEmpty())
|
|
425
|
+
{
|
|
426
|
+
TSharedPtr<FJsonValue> LegacyErr;
|
|
427
|
+
TargetActor = MCPResolveActorToken(PIEWorld, ActorPath, LegacyErr, PieSel);
|
|
428
|
+
if (!TargetActor && MCPIsAmbiguousActorError(LegacyErr)) return LegacyErr;
|
|
429
|
+
}
|
|
406
430
|
|
|
407
431
|
if (!TargetActor)
|
|
408
432
|
{
|
|
@@ -656,19 +680,127 @@ namespace
|
|
|
656
680
|
Out->SetObjectField(Field, O); return true;
|
|
657
681
|
}
|
|
658
682
|
}
|
|
683
|
+
// #885: a container is real JSON here too. The export-text fallback
|
|
684
|
+
// below renders a TArray<FString> as an empty string, so a path whose
|
|
685
|
+
// leaf is an array read back as though it held nothing.
|
|
686
|
+
if (MCPFunctionCall::IsContainerProperty(Prop))
|
|
687
|
+
{
|
|
688
|
+
Out->SetField(Field, MCPFunctionCall::ValueToJson(Prop, ValuePtr, nullptr));
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
659
691
|
FString Exported;
|
|
660
692
|
Prop->ExportTextItem_Direct(Exported, ValuePtr, nullptr, nullptr, PPF_None);
|
|
661
693
|
Out->SetStringField(Field, Exported);
|
|
662
694
|
return true;
|
|
663
695
|
}
|
|
664
696
|
|
|
697
|
+
/**
|
|
698
|
+
* #969: evaluate a path segment that resolved to a UFUNCTION, binding the
|
|
699
|
+
* literal arguments the segment carried.
|
|
700
|
+
*
|
|
701
|
+
* A read-only accessor keyed by an id (a tally by option id, a balance by
|
|
702
|
+
* currency id, an attribute by tag) is a very common shape and used to be
|
|
703
|
+
* unreachable here: anything taking a parameter was refused, and the
|
|
704
|
+
* workaround was one invoke_object_function per key with an exact
|
|
705
|
+
* objectPath, which loses the multi-instance table this action exists for.
|
|
706
|
+
*
|
|
707
|
+
* The literals are coerced by MCPJsonProperty::SetJsonOnProperty, the same
|
|
708
|
+
* setter invoke_object_function's `args` go through. There is deliberately
|
|
709
|
+
* no second coercion: a string reads into an FName, an FString, an integer,
|
|
710
|
+
* a float, a bool or an enum there already, so a keyed accessor behaves the
|
|
711
|
+
* same whichever action reaches it.
|
|
712
|
+
*/
|
|
713
|
+
static bool CallPathGetter(
|
|
714
|
+
UObject* Target,
|
|
715
|
+
UFunction* Fn,
|
|
716
|
+
const TArray<FString>& ArgLiterals,
|
|
717
|
+
TSharedPtr<FJsonObject> Out,
|
|
718
|
+
const TCHAR* FieldKey,
|
|
719
|
+
FString& OutErr)
|
|
720
|
+
{
|
|
721
|
+
FProperty* RetProp = Fn->GetReturnProperty();
|
|
722
|
+
if (!RetProp)
|
|
723
|
+
{
|
|
724
|
+
OutErr = FString::Printf(TEXT("UFUNCTION '%s' has no return value"), *Fn->GetName());
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Inputs are the parameters a caller can supply: not the return, and not
|
|
729
|
+
// a plain out param, which the function writes rather than reads.
|
|
730
|
+
TArray<FProperty*> Inputs;
|
|
731
|
+
for (TFieldIterator<FProperty> It(Fn); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
732
|
+
{
|
|
733
|
+
FProperty* Parm = *It;
|
|
734
|
+
if (Parm->PropertyFlags & CPF_ReturnParm) continue;
|
|
735
|
+
if ((Parm->PropertyFlags & CPF_OutParm) && !(Parm->PropertyFlags & CPF_ReferenceParm)) continue;
|
|
736
|
+
Inputs.Add(Parm);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (ArgLiterals.Num() != Inputs.Num())
|
|
740
|
+
{
|
|
741
|
+
TArray<FString> Signature;
|
|
742
|
+
for (FProperty* Parm : Inputs)
|
|
743
|
+
{
|
|
744
|
+
Signature.Add(FString::Printf(TEXT("%s %s"), *Parm->GetCPPType(), *Parm->GetName()));
|
|
745
|
+
}
|
|
746
|
+
OutErr = FString::Printf(
|
|
747
|
+
TEXT("UFUNCTION '%s' takes %d argument(s) (%s) but the path supplied %d. Write them into the path, e.g. '%s(key)'."),
|
|
748
|
+
*Fn->GetName(), Inputs.Num(), *FString::Join(Signature, TEXT(", ")), ArgLiterals.Num(), *Fn->GetName());
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// ParmsSize, not PropertiesSize: a Blueprint function's locals live past
|
|
753
|
+
// the parameter block and initialising them would run off the frame.
|
|
754
|
+
TArray<uint8> Frame;
|
|
755
|
+
Frame.SetNumZeroed(Fn->ParmsSize);
|
|
756
|
+
for (TFieldIterator<FProperty> It(Fn); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
757
|
+
{
|
|
758
|
+
It->InitializeValue_InContainer(Frame.GetData());
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
bool bBound = true;
|
|
762
|
+
for (int32 ArgIndex = 0; ArgIndex < Inputs.Num(); ++ArgIndex)
|
|
763
|
+
{
|
|
764
|
+
FProperty* Parm = Inputs[ArgIndex];
|
|
765
|
+
const TSharedPtr<FJsonValue> AsJson = MakeShared<FJsonValueString>(ArgLiterals[ArgIndex]);
|
|
766
|
+
FString BindErr;
|
|
767
|
+
if (!MCPJsonProperty::SetJsonOnProperty(
|
|
768
|
+
Parm, Parm->ContainerPtrToValuePtr<void>(Frame.GetData()), AsJson, BindErr))
|
|
769
|
+
{
|
|
770
|
+
OutErr = FString::Printf(
|
|
771
|
+
TEXT("UFUNCTION '%s' argument '%s': %s"), *Fn->GetName(), *Parm->GetName(), *BindErr);
|
|
772
|
+
bBound = false;
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (bBound)
|
|
778
|
+
{
|
|
779
|
+
{
|
|
780
|
+
// #806: without this guard an actor getter called against the
|
|
781
|
+
// editor world is skipped and the zeroed frame reads back as a
|
|
782
|
+
// real value.
|
|
783
|
+
FEditorScriptExecutionGuard ScriptGuard;
|
|
784
|
+
Target->ProcessEvent(Fn, Frame.GetData());
|
|
785
|
+
}
|
|
786
|
+
WritePropertyValue(Out, FieldKey, RetProp, RetProp->ContainerPtrToValuePtr<void>(Frame.GetData()));
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
MCPFunctionCall::DestroyFrame(Fn, Frame.GetData());
|
|
790
|
+
return bBound;
|
|
791
|
+
}
|
|
792
|
+
|
|
665
793
|
// Walk one dotted path starting at Root. Per segment: property hop, sub-object
|
|
666
|
-
// hop, or - at the leaf - a
|
|
794
|
+
// hop, or - at the leaf - a UFUNCTION call, which may carry literal arguments
|
|
795
|
+
// as 'GetTallyWeight(overclock)'. Writes the result onto Out.
|
|
667
796
|
static void ResolvePath(UObject* Root, const FString& Path, TSharedPtr<FJsonObject> Out, const TCHAR* FieldKey, FString& OutErr)
|
|
668
797
|
{
|
|
669
798
|
if (!Root) { OutErr = TEXT("null root"); return; }
|
|
670
799
|
TArray<FString> Parts;
|
|
671
|
-
|
|
800
|
+
// Split on dots that separate segments, not on a dot inside an argument
|
|
801
|
+
// list: 'GetWeightAt(1.5)' is one segment, and splitting it would look
|
|
802
|
+
// up a property named 'GetWeightAt(1'.
|
|
803
|
+
MCPFunctionCall::SplitPathSegments(Path, Parts);
|
|
672
804
|
if (Parts.Num() == 0) { OutErr = TEXT("empty path"); return; }
|
|
673
805
|
|
|
674
806
|
UStruct* CurStruct = Root->GetClass();
|
|
@@ -677,9 +809,24 @@ namespace
|
|
|
677
809
|
|
|
678
810
|
for (int32 i = 0; i < Parts.Num(); ++i)
|
|
679
811
|
{
|
|
680
|
-
const FString&
|
|
812
|
+
const FString& RawSeg = Parts[i];
|
|
681
813
|
const bool bLast = (i == Parts.Num() - 1);
|
|
682
|
-
|
|
814
|
+
|
|
815
|
+
// A segment may be 'Name' or 'Name(literal, literal)'. Reading the
|
|
816
|
+
// argument list off the front makes the rest of this loop the same
|
|
817
|
+
// as it always was.
|
|
818
|
+
FString Seg;
|
|
819
|
+
TArray<FString> CallArgs;
|
|
820
|
+
bool bHasArgList = false;
|
|
821
|
+
if (!MCPFunctionCall::ParseCallSegment(RawSeg, Seg, CallArgs, bHasArgList, OutErr))
|
|
822
|
+
{
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// An argument list says "call this", so no property or component of
|
|
827
|
+
// that name is considered: silently reading a property while the
|
|
828
|
+
// caller asked for a call would answer a question nobody asked.
|
|
829
|
+
FProperty* Prop = bHasArgList ? nullptr : CurStruct->FindPropertyByName(FName(*Seg));
|
|
683
830
|
|
|
684
831
|
if (bLast && Prop)
|
|
685
832
|
{
|
|
@@ -711,9 +858,9 @@ namespace
|
|
|
711
858
|
|
|
712
859
|
// No property by that name. At the head of the path, try matching an
|
|
713
860
|
// actor component by name (mirrors get_runtime_value behavior). At
|
|
714
|
-
// any later segment OR the leaf, try a
|
|
715
|
-
//
|
|
716
|
-
if (i == 0)
|
|
861
|
+
// any later segment OR the leaf, try a UFUNCTION call - that covers
|
|
862
|
+
// GetRequired() / IsPowered() and, since #969, GetTally(overclock).
|
|
863
|
+
if (i == 0 && !bHasArgList)
|
|
717
864
|
{
|
|
718
865
|
if (AActor* AsActor = Cast<AActor>(CurObject))
|
|
719
866
|
{
|
|
@@ -730,53 +877,21 @@ namespace
|
|
|
730
877
|
}
|
|
731
878
|
}
|
|
732
879
|
|
|
733
|
-
// UFUNCTION
|
|
880
|
+
// UFUNCTION getter at this segment, with or without arguments.
|
|
734
881
|
if (UFunction* Fn = CurObject ? CurObject->FindFunction(FName(*Seg)) : nullptr)
|
|
735
882
|
{
|
|
736
|
-
if (
|
|
883
|
+
if (!bLast)
|
|
737
884
|
{
|
|
738
|
-
|
|
739
|
-
FMemory::Memzero(Frame, Fn->ParmsSize);
|
|
740
|
-
for (TFieldIterator<FProperty> It(Fn); It; ++It)
|
|
741
|
-
{
|
|
742
|
-
It->InitializeValue_InContainer(Frame);
|
|
743
|
-
}
|
|
744
|
-
{
|
|
745
|
-
// #806: without this guard an actor getter called against
|
|
746
|
-
// the editor world is skipped and the zeroed frame reads
|
|
747
|
-
// back as a real value.
|
|
748
|
-
FEditorScriptExecutionGuard ScriptGuard;
|
|
749
|
-
CurObject->ProcessEvent(Fn, Frame);
|
|
750
|
-
}
|
|
751
|
-
FProperty* RetProp = Fn->GetReturnProperty();
|
|
752
|
-
if (RetProp)
|
|
753
|
-
{
|
|
754
|
-
if (bLast)
|
|
755
|
-
{
|
|
756
|
-
void* RetVal = RetProp->ContainerPtrToValuePtr<void>(Frame);
|
|
757
|
-
WritePropertyValue(Out, FieldKey, RetProp, RetVal);
|
|
758
|
-
}
|
|
759
|
-
else
|
|
760
|
-
{
|
|
761
|
-
OutErr = FString::Printf(TEXT("UFUNCTION '%s' must be the leaf segment - cannot descend into its return"), *Seg);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
else
|
|
765
|
-
{
|
|
766
|
-
OutErr = FString::Printf(TEXT("UFUNCTION '%s' has no return value"), *Seg);
|
|
767
|
-
}
|
|
768
|
-
for (TFieldIterator<FProperty> It(Fn); It; ++It)
|
|
769
|
-
{
|
|
770
|
-
It->DestroyValue_InContainer(Frame);
|
|
771
|
-
}
|
|
885
|
+
OutErr = FString::Printf(TEXT("UFUNCTION '%s' must be the leaf segment - cannot descend into its return"), *Seg);
|
|
772
886
|
return;
|
|
773
887
|
}
|
|
774
|
-
|
|
888
|
+
CallPathGetter(CurObject, Fn, CallArgs, Out, FieldKey, OutErr);
|
|
775
889
|
return;
|
|
776
890
|
}
|
|
777
891
|
|
|
778
|
-
OutErr = FString::Printf(
|
|
779
|
-
|
|
892
|
+
OutErr = FString::Printf(
|
|
893
|
+
TEXT("Segment '%s' is neither a property, component, nor a UFUNCTION on %s. A UFUNCTION that takes arguments is written '%s(value)'."),
|
|
894
|
+
*Seg, *CurStruct->GetName(), *Seg);
|
|
780
895
|
return;
|
|
781
896
|
|
|
782
897
|
NextSegment:;
|
|
@@ -983,7 +1098,7 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeFunction(const TSharedPtr<FJsonObj
|
|
|
983
1098
|
FString FunctionName;
|
|
984
1099
|
if (auto Err = RequireString(Params, TEXT("functionName"), FunctionName)) return Err;
|
|
985
1100
|
FString ActorLabel;
|
|
986
|
-
if (auto Err =
|
|
1101
|
+
if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
|
|
987
1102
|
|
|
988
1103
|
// #778: this used GEditor->GetPIEWorldContext(), which is always the
|
|
989
1104
|
// primary (server) context, so 'pieInstance' could never reach it and a
|
|
@@ -1008,11 +1123,14 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeFunction(const TSharedPtr<FJsonObj
|
|
|
1008
1123
|
// fixed label -> name -> path order, and a miss is an error. There is no
|
|
1009
1124
|
// class-default fallback here and there must never be one: a default object
|
|
1010
1125
|
// answers every call with default state, which reads as success.
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1126
|
+
// #983: actorPath wins when given, and a label that names more than one
|
|
1127
|
+
// actor is refused rather than invoked on whichever came first.
|
|
1128
|
+
FMCPActorSelector TargetSel;
|
|
1129
|
+
TargetSel.Match = EMCPActorMatch::LabelNameOrPath;
|
|
1130
|
+
TargetSel.WorldLabel = *WorldLabel;
|
|
1131
|
+
TSharedPtr<FJsonValue> TargetErr;
|
|
1132
|
+
AActor* Target = MCPResolveActor(World, Params, TargetErr, TargetSel);
|
|
1133
|
+
if (!Target) return TargetErr;
|
|
1016
1134
|
// Defensive: the lookup iterates placed actors, so neither of these can fire
|
|
1017
1135
|
// today. They exist so that a future change which lets an archetype or an
|
|
1018
1136
|
// actor from another world through fails loudly instead of quietly
|
|
@@ -1125,7 +1243,22 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeFunction(const TSharedPtr<FJsonObj
|
|
|
1125
1243
|
continue;
|
|
1126
1244
|
}
|
|
1127
1245
|
|
|
1128
|
-
|
|
1246
|
+
// #983: an actor argument is an actor selector like any other, so a
|
|
1247
|
+
// duplicated label is refused rather than passed as whichever copy
|
|
1248
|
+
// the iterator reached first.
|
|
1249
|
+
TArray<AActor*> ArgMatches;
|
|
1250
|
+
MCPCollectActorsByToken(World, ActorArgLabel, EMCPActorMatch::LabelNameOrPath, ArgMatches);
|
|
1251
|
+
if (ArgMatches.Num() > 1)
|
|
1252
|
+
{
|
|
1253
|
+
for (TFieldIterator<FProperty> CleanupIt(Func); CleanupIt && (CleanupIt->PropertyFlags & CPF_Parm); ++CleanupIt)
|
|
1254
|
+
{
|
|
1255
|
+
CleanupIt->DestroyValue_InContainer(ParamBuf.GetData());
|
|
1256
|
+
}
|
|
1257
|
+
return MCPAmbiguousActorError(
|
|
1258
|
+
ActorArgLabel, TEXT("actorArgs"), TEXT("actorPath"),
|
|
1259
|
+
MCPDescribeActorMatchTier(ActorArgLabel, ArgMatches[0]), ArgMatches);
|
|
1260
|
+
}
|
|
1261
|
+
AActor* RefActor = ArgMatches.Num() == 1 ? ArgMatches[0] : nullptr;
|
|
1129
1262
|
if (!RefActor)
|
|
1130
1263
|
{
|
|
1131
1264
|
for (TFieldIterator<FProperty> CleanupIt(Func); CleanupIt && (CleanupIt->PropertyFlags & CPF_Parm); ++CleanupIt)
|
|
@@ -1169,18 +1302,15 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeFunction(const TSharedPtr<FJsonObj
|
|
|
1169
1302
|
// ProcessEvent can run arbitrary game code, including code that tears down
|
|
1170
1303
|
// the world and collects garbage, and CallTarget is read again below.
|
|
1171
1304
|
FGCObjectScopeGuard CallTargetGuard(CallTarget);
|
|
1172
|
-
|
|
1173
|
-
//
|
|
1174
|
-
//
|
|
1175
|
-
//
|
|
1176
|
-
//
|
|
1177
|
-
//
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
FEditorScriptExecutionGuard ScriptGuard;
|
|
1182
|
-
CallTarget->ProcessEvent(Func, ParamBuf.GetData());
|
|
1183
|
-
}
|
|
1305
|
+
|
|
1306
|
+
// #973: read the callspace BEFORE the guard opens. That is the only moment
|
|
1307
|
+
// it is observable: inside the guard GAllowActorScriptExecutionInEditor
|
|
1308
|
+
// makes AActor::GetFunctionCallspace answer Local in its first branch, so a
|
|
1309
|
+
// UFUNCTION(Server) runs its implementation on this copy instead of being
|
|
1310
|
+
// sent, and the result used to say nothing about it.
|
|
1311
|
+
FString NaturalCallspace;
|
|
1312
|
+
const bool bCallspaceForcedLocal =
|
|
1313
|
+
MCPFunctionCall::WouldForceNetCallspaceLocal(CallTarget, Func, NaturalCallspace);
|
|
1184
1314
|
|
|
1185
1315
|
auto Result = MCPSuccess();
|
|
1186
1316
|
Result->SetStringField(TEXT("actorLabel"), ActorLabel);
|
|
@@ -1193,36 +1323,60 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeFunction(const TSharedPtr<FJsonObj
|
|
|
1193
1323
|
if (!ComponentName.IsEmpty()) Result->SetStringField(TEXT("component"), ComponentName);
|
|
1194
1324
|
Result->SetStringField(TEXT("functionName"), FunctionName);
|
|
1195
1325
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1326
|
+
// #973: the opt-in escape from the override above. Queued for the next
|
|
1327
|
+
// engine tick, the send happens after the guard's scope has ended and the
|
|
1328
|
+
// call routes the way it would from game code.
|
|
1329
|
+
if (OptionalBool(Params, TEXT("deferToNextTick"), false))
|
|
1198
1330
|
{
|
|
1199
|
-
|
|
1200
|
-
if (
|
|
1331
|
+
Result->SetBoolField(TEXT("deferred"), true);
|
|
1332
|
+
if (!NaturalCallspace.IsEmpty())
|
|
1201
1333
|
{
|
|
1202
|
-
|
|
1203
|
-
// the call destroyed would be dereferenced by ExportTextItem_Direct.
|
|
1204
|
-
// The scope guard above covers the target, not the results.
|
|
1205
|
-
if (FObjectPropertyBase* OP = CastField<FObjectPropertyBase>(P))
|
|
1206
|
-
{
|
|
1207
|
-
UObject* Out = OP->GetObjectPropertyValue(OP->ContainerPtrToValuePtr<void>(ParamBuf.GetData()));
|
|
1208
|
-
if (!Out) { OutVals->SetStringField(P->GetName(), TEXT("None")); continue; }
|
|
1209
|
-
if (!IsValid(Out))
|
|
1210
|
-
{
|
|
1211
|
-
OutVals->SetStringField(P->GetName(), TEXT("(collected during the call)"));
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
FString S;
|
|
1216
|
-
P->ExportTextItem_Direct(S, P->ContainerPtrToValuePtr<void>(ParamBuf.GetData()), nullptr, CallTarget, PPF_None);
|
|
1217
|
-
OutVals->SetStringField(P->GetName(), S);
|
|
1334
|
+
Result->SetStringField(TEXT("netCallspace"), NaturalCallspace);
|
|
1218
1335
|
}
|
|
1336
|
+
Result->SetStringField(TEXT("note"), TEXT(
|
|
1337
|
+
"Queued for the next engine tick, outside the editor script-execution guard, so a replicated function "
|
|
1338
|
+
"routes through GetFunctionCallspace normally instead of being forced to Local. Return and out parameters "
|
|
1339
|
+
"are not reported: the response is written before the call runs. Read the effect back afterwards with "
|
|
1340
|
+
"editor(get_runtime_values) or editor(get_object_properties)."));
|
|
1341
|
+
if (WorldLabel == TEXT("editor"))
|
|
1342
|
+
{
|
|
1343
|
+
Result->SetStringField(TEXT("warning"), TEXT(
|
|
1344
|
+
"The target is in the editor world, whose actors were never initialised for play. Outside the guard "
|
|
1345
|
+
"AActor::ProcessEvent will skip the call entirely. deferToNextTick is for a live PIE session."));
|
|
1346
|
+
}
|
|
1347
|
+
MCPFunctionCall::DeferProcessEventToNextTick(CallTarget, Func, MoveTemp(ParamBuf));
|
|
1348
|
+
return MCPResult(Result);
|
|
1219
1349
|
}
|
|
1220
|
-
Result->SetObjectField(TEXT("returnValues"), OutVals);
|
|
1221
1350
|
|
|
1222
|
-
|
|
1351
|
+
// #806: AActor::ProcessEvent refuses to run script in a world whose actors
|
|
1352
|
+
// were never initialised for play, which is every editor world, unless the
|
|
1353
|
+
// function is marked CallInEditor. The refusal is silent: the parameter
|
|
1354
|
+
// frame stays zero-initialised and those zeros are then exported as if they
|
|
1355
|
+
// were the answer, so K2_GetActorLocation on a placed actor reported
|
|
1356
|
+
// (X=0,Y=0,Z=0) with success. FEditorScriptExecutionGuard opens the same
|
|
1357
|
+
// gate the editor itself uses for CallInEditor functions, for this call
|
|
1358
|
+
// only, so the placed instance actually runs the function.
|
|
1359
|
+
{
|
|
1360
|
+
FEditorScriptExecutionGuard ScriptGuard;
|
|
1361
|
+
CallTarget->ProcessEvent(Func, ParamBuf.GetData());
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if (bCallspaceForcedLocal)
|
|
1223
1365
|
{
|
|
1224
|
-
|
|
1366
|
+
Result->SetStringField(TEXT("netCallspace"), NaturalCallspace);
|
|
1367
|
+
Result->SetBoolField(TEXT("callspaceForcedLocal"), true);
|
|
1368
|
+
Result->SetStringField(TEXT("warning"),
|
|
1369
|
+
MCPFunctionCall::DescribeForcedLocalCallspace(FunctionName, NaturalCallspace));
|
|
1225
1370
|
}
|
|
1371
|
+
|
|
1372
|
+
// #885: containers come back as real JSON. Export text renders a
|
|
1373
|
+
// TArray<FString> return as an empty string, which made every
|
|
1374
|
+
// array-returning accessor unreadable through the bridge.
|
|
1375
|
+
TSharedPtr<FJsonObject> OutVals = MakeShared<FJsonObject>();
|
|
1376
|
+
MCPFunctionCall::WriteOutputs(OutVals, Func, ParamBuf.GetData(), CallTarget);
|
|
1377
|
+
Result->SetObjectField(TEXT("returnValues"), OutVals);
|
|
1378
|
+
|
|
1379
|
+
MCPFunctionCall::DestroyFrame(Func, ParamBuf.GetData());
|
|
1226
1380
|
return MCPResult(Result);
|
|
1227
1381
|
}
|
|
1228
1382
|
|
|
@@ -1245,18 +1399,23 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeStaticFunction(const TSharedPtr<FJ
|
|
|
1245
1399
|
FString FunctionName;
|
|
1246
1400
|
if (auto Err = RequireString(Params, TEXT("functionName"), FunctionName)) return Err;
|
|
1247
1401
|
|
|
1402
|
+
// #971: route through the shared resolver, so world=editor|pie|game|auto
|
|
1403
|
+
// and pieInstance select here exactly as they do for invoke_function. The
|
|
1404
|
+
// old code special-cased the literal string "pie" and sent everything else
|
|
1405
|
+
// to the editor world, which has no GameInstance: a server-authoritative
|
|
1406
|
+
// entry point implemented as a static that looks a UGameInstanceSubsystem up
|
|
1407
|
+
// off its world context could not be exercised in PIE at all.
|
|
1248
1408
|
const FString WorldScope = OptionalString(Params, TEXT("world"), TEXT("editor")).ToLower();
|
|
1249
|
-
UWorld* World =
|
|
1250
|
-
if (
|
|
1251
|
-
{
|
|
1252
|
-
World = ResolveWorldFromParams(Params, TEXT("pie"));
|
|
1253
|
-
if (!World) return MCPError(TEXT("PIE not running - cannot invoke against PIE world"));
|
|
1254
|
-
}
|
|
1255
|
-
else
|
|
1409
|
+
UWorld* World = ResolveWorldFromParams(Params, *WorldScope);
|
|
1410
|
+
if (!World)
|
|
1256
1411
|
{
|
|
1257
|
-
|
|
1258
|
-
|
|
1412
|
+
return MCPError(WorldScope == TEXT("pie") || WorldScope == TEXT("game")
|
|
1413
|
+
? TEXT("PIE not running (or no such pieInstance) - cannot invoke against a PIE world. See editor(list_pie_instances).")
|
|
1414
|
+
: TEXT("No editor world available"));
|
|
1259
1415
|
}
|
|
1416
|
+
// Describe the world that was actually resolved, not the requested scope:
|
|
1417
|
+
// world="auto" resolves to PIE when a session is running.
|
|
1418
|
+
const FString WorldLabel = World->IsPlayInEditor() ? TEXT("PIE") : TEXT("editor");
|
|
1260
1419
|
|
|
1261
1420
|
// Resolve the function-library class: a /Script/Module.Class path, or a bare
|
|
1262
1421
|
// class name (with or without the leading U).
|
|
@@ -1332,11 +1491,22 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeStaticFunction(const TSharedPtr<FJ
|
|
|
1332
1491
|
if (!OP) continue;
|
|
1333
1492
|
FString ActorArgLabel;
|
|
1334
1493
|
if (!(*ActorArgObj)->TryGetStringField(P->GetName(), ActorArgLabel) || ActorArgLabel.IsEmpty()) continue;
|
|
1335
|
-
|
|
1494
|
+
// #983: refuse a duplicated label rather than passing whichever
|
|
1495
|
+
// copy the actor iterator reached first.
|
|
1496
|
+
TArray<AActor*> ArgMatches;
|
|
1497
|
+
MCPCollectActorsByToken(World, ActorArgLabel, EMCPActorMatch::LabelNameOrPath, ArgMatches);
|
|
1498
|
+
if (ArgMatches.Num() > 1)
|
|
1499
|
+
{
|
|
1500
|
+
Cleanup();
|
|
1501
|
+
return MCPAmbiguousActorError(
|
|
1502
|
+
ActorArgLabel, TEXT("actorArgs"), TEXT("actorPath"),
|
|
1503
|
+
MCPDescribeActorMatchTier(ActorArgLabel, ArgMatches[0]), ArgMatches);
|
|
1504
|
+
}
|
|
1505
|
+
AActor* RefActor = ArgMatches.Num() == 1 ? ArgMatches[0] : nullptr;
|
|
1336
1506
|
if (!RefActor)
|
|
1337
1507
|
{
|
|
1338
1508
|
Cleanup();
|
|
1339
|
-
return MCPError(FString::Printf(TEXT("actorArgs[%s]: actor '%s' not found in %s world"), *P->GetName(), *ActorArgLabel,
|
|
1509
|
+
return MCPError(FString::Printf(TEXT("actorArgs[%s]: actor '%s' not found in %s world"), *P->GetName(), *ActorArgLabel, *WorldLabel));
|
|
1340
1510
|
}
|
|
1341
1511
|
if (!RefActor->IsA(OP->PropertyClass))
|
|
1342
1512
|
{
|
|
@@ -1362,8 +1532,22 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeStaticFunction(const TSharedPtr<FJ
|
|
|
1362
1532
|
|
|
1363
1533
|
// World-context injection: static library functions commonly take a UObject*
|
|
1364
1534
|
// WorldContextObject. Fill any unset object param matching worldContextParam
|
|
1365
|
-
// (or
|
|
1535
|
+
// (or the name the function's own WorldContext metadata gives, or a name
|
|
1536
|
+
// containing WorldContext) with the world resolved above.
|
|
1537
|
+
//
|
|
1538
|
+
// #971: worldContextParam only ever chose WHICH parameter to fill. What goes
|
|
1539
|
+
// into it is the selected world, so the parameter that decides PIE versus
|
|
1540
|
+
// editor is `world`, and the metadata lookup means a library that names its
|
|
1541
|
+
// context parameter something else is still recognised.
|
|
1366
1542
|
const FString WcParam = OptionalString(Params, TEXT("worldContextParam"), TEXT(""));
|
|
1543
|
+
FString MetaWcParam;
|
|
1544
|
+
#if WITH_METADATA
|
|
1545
|
+
if (const FString* Declared = Func->FindMetaData(TEXT("WorldContext")))
|
|
1546
|
+
{
|
|
1547
|
+
MetaWcParam = *Declared;
|
|
1548
|
+
}
|
|
1549
|
+
#endif
|
|
1550
|
+
FString FilledWcParam;
|
|
1367
1551
|
for (TFieldIterator<FProperty> It(Func); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
1368
1552
|
{
|
|
1369
1553
|
FProperty* P = *It;
|
|
@@ -1371,13 +1555,17 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeStaticFunction(const TSharedPtr<FJ
|
|
|
1371
1555
|
FObjectProperty* OP = CastField<FObjectProperty>(P);
|
|
1372
1556
|
if (!OP) continue;
|
|
1373
1557
|
const FString PName = P->GetName();
|
|
1374
|
-
const bool bIsWc =
|
|
1558
|
+
const bool bIsWc =
|
|
1559
|
+
(!WcParam.IsEmpty() && PName == WcParam) ||
|
|
1560
|
+
(!MetaWcParam.IsEmpty() && PName == MetaWcParam) ||
|
|
1561
|
+
PName.Contains(TEXT("WorldContext"));
|
|
1375
1562
|
if (!bIsWc) continue;
|
|
1376
1563
|
void* Addr = P->ContainerPtrToValuePtr<void>(ParamBuf.GetData());
|
|
1377
1564
|
if (OP->GetObjectPropertyValue(Addr) != nullptr) continue;
|
|
1378
1565
|
if (World->IsA(OP->PropertyClass) || OP->PropertyClass == UObject::StaticClass())
|
|
1379
1566
|
{
|
|
1380
1567
|
OP->SetObjectPropertyValue(Addr, World);
|
|
1568
|
+
FilledWcParam = PName;
|
|
1381
1569
|
}
|
|
1382
1570
|
}
|
|
1383
1571
|
|
|
@@ -1387,31 +1575,21 @@ TSharedPtr<FJsonValue> FEditorHandlers::InvokeStaticFunction(const TSharedPtr<FJ
|
|
|
1387
1575
|
auto Result = MCPSuccess();
|
|
1388
1576
|
Result->SetStringField(TEXT("className"), LibClass->GetName());
|
|
1389
1577
|
Result->SetStringField(TEXT("functionName"), FunctionName);
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1578
|
+
// #971: say which world the call actually ran against and which parameter
|
|
1579
|
+
// carried it. "subsystem unavailable" from a static that looks a
|
|
1580
|
+
// GameInstance subsystem up off its context is unreadable without this.
|
|
1581
|
+
Result->SetStringField(TEXT("world"), WorldLabel);
|
|
1582
|
+
Result->SetStringField(TEXT("worldPath"), World->GetPathName());
|
|
1583
|
+
Result->SetStringField(TEXT("netMode"), DescribePIENetMode(World));
|
|
1584
|
+
if (!FilledWcParam.IsEmpty())
|
|
1393
1585
|
{
|
|
1394
|
-
|
|
1395
|
-
if (P->PropertyFlags & (CPF_ReturnParm | CPF_OutParm))
|
|
1396
|
-
{
|
|
1397
|
-
// ParamBuf is raw bytes and invisible to GC, so an object out-param
|
|
1398
|
-
// the call destroyed would be dereferenced by ExportTextItem_Direct.
|
|
1399
|
-
// The scope guard above covers the CDO, not the results.
|
|
1400
|
-
if (FObjectPropertyBase* OP = CastField<FObjectPropertyBase>(P))
|
|
1401
|
-
{
|
|
1402
|
-
UObject* Out = OP->GetObjectPropertyValue(OP->ContainerPtrToValuePtr<void>(ParamBuf.GetData()));
|
|
1403
|
-
if (!Out) { OutVals->SetStringField(P->GetName(), TEXT("None")); continue; }
|
|
1404
|
-
if (!IsValid(Out))
|
|
1405
|
-
{
|
|
1406
|
-
OutVals->SetStringField(P->GetName(), TEXT("(collected during the call)"));
|
|
1407
|
-
continue;
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
FString S;
|
|
1411
|
-
P->ExportTextItem_Direct(S, P->ContainerPtrToValuePtr<void>(ParamBuf.GetData()), nullptr, CDO, PPF_None);
|
|
1412
|
-
OutVals->SetStringField(P->GetName(), S);
|
|
1413
|
-
}
|
|
1586
|
+
Result->SetStringField(TEXT("worldContextParam"), FilledWcParam);
|
|
1414
1587
|
}
|
|
1588
|
+
|
|
1589
|
+
// #885: containers come back as real JSON, scalars and structs keep their
|
|
1590
|
+
// export-text spelling. See MCPFunctionCall::OutputToJson.
|
|
1591
|
+
TSharedPtr<FJsonObject> OutVals = MakeShared<FJsonObject>();
|
|
1592
|
+
MCPFunctionCall::WriteOutputs(OutVals, Func, ParamBuf.GetData(), CDO);
|
|
1415
1593
|
Result->SetObjectField(TEXT("returnValues"), OutVals);
|
|
1416
1594
|
|
|
1417
1595
|
Cleanup();
|