ue-mcp 1.2.4 → 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/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/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/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +21 -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
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
2
|
+
|
|
3
|
+
#include "HandlerRegistry.h"
|
|
4
|
+
#include "Handlers/LevelHandlers.h"
|
|
5
|
+
#include "HandlerUtils.h"
|
|
6
|
+
#include "Misc/AutomationTest.h"
|
|
7
|
+
|
|
8
|
+
#include "Components/InstancedStaticMeshComponent.h"
|
|
9
|
+
#include "Components/StaticMeshComponent.h"
|
|
10
|
+
#include "Editor.h"
|
|
11
|
+
#include "Engine/Engine.h"
|
|
12
|
+
#include "Engine/StaticMesh.h"
|
|
13
|
+
#include "Engine/World.h"
|
|
14
|
+
#include "GameFramework/Actor.h"
|
|
15
|
+
#include "UObject/Package.h"
|
|
16
|
+
#include "UObject/UObjectGlobals.h"
|
|
17
|
+
|
|
18
|
+
namespace
|
|
19
|
+
{
|
|
20
|
+
constexpr const TCHAR* StaticMeshUsageMethod = TEXT("summarize_static_mesh_usage");
|
|
21
|
+
|
|
22
|
+
class FScopedEditorTestWorld
|
|
23
|
+
{
|
|
24
|
+
public:
|
|
25
|
+
FScopedEditorTestWorld()
|
|
26
|
+
{
|
|
27
|
+
if (!GEditor)
|
|
28
|
+
{
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
OriginalWorld = GEditor->GetEditorWorldContext().World();
|
|
33
|
+
const FName WorldName = MakeUniqueObjectName(
|
|
34
|
+
GetTransientPackage(),
|
|
35
|
+
UWorld::StaticClass(),
|
|
36
|
+
FName(TEXT("UEMCP_StaticMeshUsageTestWorld")));
|
|
37
|
+
const UWorld::InitializationValues InitializationValues = UWorld::InitializationValues()
|
|
38
|
+
.InitializeScenes(false)
|
|
39
|
+
.AllowAudioPlayback(false)
|
|
40
|
+
.RequiresHitProxies(false)
|
|
41
|
+
.CreatePhysicsScene(false)
|
|
42
|
+
.CreateNavigation(false)
|
|
43
|
+
.CreateAISystem(false)
|
|
44
|
+
.ShouldSimulatePhysics(false)
|
|
45
|
+
.EnableTraceCollision(false)
|
|
46
|
+
.SetTransactional(false)
|
|
47
|
+
.CreateFXSystem(false)
|
|
48
|
+
.CreateWorldPartition(false);
|
|
49
|
+
|
|
50
|
+
TestWorld = UWorld::CreateWorld(
|
|
51
|
+
EWorldType::Editor,
|
|
52
|
+
false,
|
|
53
|
+
WorldName,
|
|
54
|
+
GetTransientPackage(),
|
|
55
|
+
true,
|
|
56
|
+
ERHIFeatureLevel::Num,
|
|
57
|
+
&InitializationValues);
|
|
58
|
+
if (TestWorld)
|
|
59
|
+
{
|
|
60
|
+
GEditor->GetEditorWorldContext().SetCurrentWorld(TestWorld);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
~FScopedEditorTestWorld()
|
|
65
|
+
{
|
|
66
|
+
if (GEditor && TestWorld)
|
|
67
|
+
{
|
|
68
|
+
GEditor->GetEditorWorldContext().SetCurrentWorld(OriginalWorld);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (TestWorld)
|
|
72
|
+
{
|
|
73
|
+
TestWorld->DestroyWorld(false);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
UWorld* Get() const
|
|
78
|
+
{
|
|
79
|
+
return TestWorld;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private:
|
|
83
|
+
UWorld* OriginalWorld = nullptr;
|
|
84
|
+
UWorld* TestWorld = nullptr;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
AActor* SpawnMeshUsageTestActor(UWorld* World, const TCHAR* Name, const TCHAR* Label)
|
|
88
|
+
{
|
|
89
|
+
if (!World)
|
|
90
|
+
{
|
|
91
|
+
return nullptr;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
FActorSpawnParameters SpawnParameters;
|
|
95
|
+
SpawnParameters.Name = MakeUniqueObjectName(
|
|
96
|
+
World->PersistentLevel,
|
|
97
|
+
AActor::StaticClass(),
|
|
98
|
+
FName(Name));
|
|
99
|
+
SpawnParameters.ObjectFlags = RF_Transient;
|
|
100
|
+
AActor* Actor = World->SpawnActor<AActor>(
|
|
101
|
+
AActor::StaticClass(),
|
|
102
|
+
FTransform::Identity,
|
|
103
|
+
SpawnParameters);
|
|
104
|
+
if (Actor)
|
|
105
|
+
{
|
|
106
|
+
Actor->SetActorLabel(Label, false);
|
|
107
|
+
}
|
|
108
|
+
return Actor;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
template <typename ComponentType>
|
|
112
|
+
ComponentType* AddTransientMeshComponent(
|
|
113
|
+
AActor* Actor,
|
|
114
|
+
const TCHAR* Name,
|
|
115
|
+
UStaticMesh* Mesh)
|
|
116
|
+
{
|
|
117
|
+
if (!Actor)
|
|
118
|
+
{
|
|
119
|
+
return nullptr;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ComponentType* Component = NewObject<ComponentType>(Actor, Name, RF_Transient);
|
|
123
|
+
Actor->AddInstanceComponent(Component);
|
|
124
|
+
Component->SetStaticMesh(Mesh);
|
|
125
|
+
return Component;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
TSharedPtr<FJsonObject> ExecuteStaticMeshUsage(
|
|
129
|
+
FMCPHandlerRegistry& Registry,
|
|
130
|
+
int32 MaxResults,
|
|
131
|
+
bool bIncludeOccurrences,
|
|
132
|
+
int32 MaxOccurrences)
|
|
133
|
+
{
|
|
134
|
+
TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
|
|
135
|
+
Params->SetStringField(TEXT("world"), TEXT("editor"));
|
|
136
|
+
Params->SetNumberField(TEXT("maxResults"), MaxResults);
|
|
137
|
+
Params->SetBoolField(TEXT("includeOccurrences"), bIncludeOccurrences);
|
|
138
|
+
Params->SetNumberField(TEXT("maxOccurrences"), MaxOccurrences);
|
|
139
|
+
|
|
140
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(StaticMeshUsageMethod, Params);
|
|
141
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
142
|
+
{
|
|
143
|
+
return nullptr;
|
|
144
|
+
}
|
|
145
|
+
return Response->AsObject();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
TSharedPtr<FJsonObject> FindMeshResult(
|
|
149
|
+
const TArray<TSharedPtr<FJsonValue>>& Meshes,
|
|
150
|
+
const FString& MeshPath)
|
|
151
|
+
{
|
|
152
|
+
for (const TSharedPtr<FJsonValue>& MeshValue : Meshes)
|
|
153
|
+
{
|
|
154
|
+
if (!MeshValue.IsValid() || MeshValue->Type != EJson::Object)
|
|
155
|
+
{
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const TSharedPtr<FJsonObject>& Mesh = MeshValue->AsObject();
|
|
160
|
+
if (Mesh.IsValid() && Mesh->GetStringField(TEXT("meshPath")) == MeshPath)
|
|
161
|
+
{
|
|
162
|
+
return Mesh;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return TSharedPtr<FJsonObject>();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
170
|
+
FLevelStaticMeshUsageRegistrationTest,
|
|
171
|
+
"UE.MCP.Level.SummarizeStaticMeshUsage.RegistrationAndWorldValidation",
|
|
172
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
173
|
+
|
|
174
|
+
bool FLevelStaticMeshUsageRegistrationTest::RunTest(const FString& Parameters)
|
|
175
|
+
{
|
|
176
|
+
FMCPHandlerRegistry Registry;
|
|
177
|
+
FLevelHandlers::RegisterHandlers(Registry);
|
|
178
|
+
TestTrue(TEXT("static-mesh usage handler is registered"), Registry.HasHandler(StaticMeshUsageMethod));
|
|
179
|
+
|
|
180
|
+
TSharedPtr<FJsonObject> InvalidWorldParams = MakeShared<FJsonObject>();
|
|
181
|
+
InvalidWorldParams->SetStringField(TEXT("world"), TEXT("auto"));
|
|
182
|
+
const TSharedPtr<FJsonValue> InvalidWorldResponse = Registry.ExecuteHandler(
|
|
183
|
+
StaticMeshUsageMethod,
|
|
184
|
+
InvalidWorldParams);
|
|
185
|
+
TestTrue(
|
|
186
|
+
TEXT("an invalid editor/PIE scope returns an object"),
|
|
187
|
+
InvalidWorldResponse.IsValid() && InvalidWorldResponse->Type == EJson::Object);
|
|
188
|
+
if (InvalidWorldResponse.IsValid() && InvalidWorldResponse->Type == EJson::Object)
|
|
189
|
+
{
|
|
190
|
+
const TSharedPtr<FJsonObject> Result = InvalidWorldResponse->AsObject();
|
|
191
|
+
TestFalse(TEXT("an invalid world scope is unsuccessful"), Result->GetBoolField(TEXT("success")));
|
|
192
|
+
TestTrue(
|
|
193
|
+
TEXT("the scope error names both supported choices"),
|
|
194
|
+
Result->GetStringField(TEXT("error")).Contains(TEXT("editor")) &&
|
|
195
|
+
Result->GetStringField(TEXT("error")).Contains(TEXT("pie")));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// The handler must not silently substitute the editor world when PIE was
|
|
199
|
+
// explicitly requested. Automation normally runs outside PIE; retain the
|
|
200
|
+
// invalid-scope assertion above when a caller deliberately runs it in PIE.
|
|
201
|
+
if (GetPIEWorld() == nullptr)
|
|
202
|
+
{
|
|
203
|
+
TSharedPtr<FJsonObject> PieParams = MakeShared<FJsonObject>();
|
|
204
|
+
PieParams->SetStringField(TEXT("world"), TEXT("pie"));
|
|
205
|
+
const TSharedPtr<FJsonValue> PieResponse = Registry.ExecuteHandler(StaticMeshUsageMethod, PieParams);
|
|
206
|
+
TestTrue(
|
|
207
|
+
TEXT("missing PIE returns an object"),
|
|
208
|
+
PieResponse.IsValid() && PieResponse->Type == EJson::Object);
|
|
209
|
+
if (PieResponse.IsValid() && PieResponse->Type == EJson::Object)
|
|
210
|
+
{
|
|
211
|
+
const TSharedPtr<FJsonObject> Result = PieResponse->AsObject();
|
|
212
|
+
TestFalse(TEXT("missing PIE is unsuccessful"), Result->GetBoolField(TEXT("success")));
|
|
213
|
+
TestTrue(
|
|
214
|
+
TEXT("missing PIE is reported as the requested scope"),
|
|
215
|
+
Result->GetStringField(TEXT("error")).Contains(TEXT("pie")));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
223
|
+
FLevelStaticMeshUsageAggregationTest,
|
|
224
|
+
"UE.MCP.Level.SummarizeStaticMeshUsage.AggregationOrderingAndBounds",
|
|
225
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
226
|
+
|
|
227
|
+
bool FLevelStaticMeshUsageAggregationTest::RunTest(const FString& Parameters)
|
|
228
|
+
{
|
|
229
|
+
FScopedEditorTestWorld TestWorldScope;
|
|
230
|
+
UWorld* World = TestWorldScope.Get();
|
|
231
|
+
if (!TestNotNull(TEXT("a transient editor world was created"), World))
|
|
232
|
+
{
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const FString UniqueSuffix = FGuid::NewGuid().ToString(EGuidFormats::Digits);
|
|
237
|
+
UStaticMesh* AlphaMesh = NewObject<UStaticMesh>(
|
|
238
|
+
GetTransientPackage(),
|
|
239
|
+
*(FString(TEXT("UEMCP_Alpha_")) + UniqueSuffix),
|
|
240
|
+
RF_Transient);
|
|
241
|
+
UStaticMesh* BetaMesh = NewObject<UStaticMesh>(
|
|
242
|
+
GetTransientPackage(),
|
|
243
|
+
*(FString(TEXT("UEMCP_Beta_")) + UniqueSuffix),
|
|
244
|
+
RF_Transient);
|
|
245
|
+
UStaticMesh* ZuluMesh = NewObject<UStaticMesh>(
|
|
246
|
+
GetTransientPackage(),
|
|
247
|
+
*(FString(TEXT("UEMCP_Zulu_")) + UniqueSuffix),
|
|
248
|
+
RF_Transient);
|
|
249
|
+
TestNotNull(TEXT("alpha mesh fixture exists"), AlphaMesh);
|
|
250
|
+
TestNotNull(TEXT("beta mesh fixture exists"), BetaMesh);
|
|
251
|
+
TestNotNull(TEXT("zulu mesh fixture exists"), ZuluMesh);
|
|
252
|
+
if (!AlphaMesh || !BetaMesh || !ZuluMesh)
|
|
253
|
+
{
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
AActor* ActorA = SpawnMeshUsageTestActor(World, TEXT("UEMCP_Actor_A"), TEXT("Usage Actor A"));
|
|
258
|
+
AActor* ActorB = SpawnMeshUsageTestActor(World, TEXT("UEMCP_Actor_B"), TEXT("Usage Actor B"));
|
|
259
|
+
if (!TestNotNull(TEXT("first fixture actor exists"), ActorA) ||
|
|
260
|
+
!TestNotNull(TEXT("second fixture actor exists"), ActorB))
|
|
261
|
+
{
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
UStaticMeshComponent* AlphaPlainA =
|
|
266
|
+
AddTransientMeshComponent<UStaticMeshComponent>(ActorA, TEXT("CompAlphaAPlain"), AlphaMesh);
|
|
267
|
+
UInstancedStaticMeshComponent* AlphaInstances =
|
|
268
|
+
AddTransientMeshComponent<UInstancedStaticMeshComponent>(
|
|
269
|
+
ActorA,
|
|
270
|
+
TEXT("CompAlphaBInstanced"),
|
|
271
|
+
AlphaMesh);
|
|
272
|
+
UInstancedStaticMeshComponent* BetaInstances =
|
|
273
|
+
AddTransientMeshComponent<UInstancedStaticMeshComponent>(
|
|
274
|
+
ActorA,
|
|
275
|
+
TEXT("CompBeta"),
|
|
276
|
+
BetaMesh);
|
|
277
|
+
if (!TestNotNull(TEXT("plain alpha component exists"), AlphaPlainA) ||
|
|
278
|
+
!TestNotNull(TEXT("instanced alpha component exists"), AlphaInstances) ||
|
|
279
|
+
!TestNotNull(TEXT("instanced beta component exists"), BetaInstances))
|
|
280
|
+
{
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
AlphaInstances->AddInstance(FTransform(FVector(10.0, 0.0, 0.0)));
|
|
284
|
+
AlphaInstances->AddInstance(FTransform(FVector(20.0, 0.0, 0.0)));
|
|
285
|
+
for (int32 Index = 0; Index < 5; ++Index)
|
|
286
|
+
{
|
|
287
|
+
BetaInstances->AddInstance(FTransform(FVector(0.0, Index * 10.0, 0.0)));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
UInstancedStaticMeshComponent* ZuluZeroInstances =
|
|
291
|
+
AddTransientMeshComponent<UInstancedStaticMeshComponent>(
|
|
292
|
+
ActorA,
|
|
293
|
+
TEXT("CompZuluZeroInstances"),
|
|
294
|
+
ZuluMesh);
|
|
295
|
+
UStaticMeshComponent* NullMeshComponent =
|
|
296
|
+
AddTransientMeshComponent<UStaticMeshComponent>(ActorA, TEXT("CompNullMesh"), nullptr);
|
|
297
|
+
UStaticMeshComponent* AlphaPlainB =
|
|
298
|
+
AddTransientMeshComponent<UStaticMeshComponent>(ActorB, TEXT("CompAlphaCPlain"), AlphaMesh);
|
|
299
|
+
UInstancedStaticMeshComponent* ZuluInstances =
|
|
300
|
+
AddTransientMeshComponent<UInstancedStaticMeshComponent>(
|
|
301
|
+
ActorB,
|
|
302
|
+
TEXT("CompZuluFilled"),
|
|
303
|
+
ZuluMesh);
|
|
304
|
+
if (!TestNotNull(TEXT("zero-instance zulu component exists"), ZuluZeroInstances) ||
|
|
305
|
+
!TestNotNull(TEXT("null-mesh component exists"), NullMeshComponent) ||
|
|
306
|
+
!TestNotNull(TEXT("second plain alpha component exists"), AlphaPlainB) ||
|
|
307
|
+
!TestNotNull(TEXT("filled zulu component exists"), ZuluInstances))
|
|
308
|
+
{
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
for (int32 Index = 0; Index < 4; ++Index)
|
|
312
|
+
{
|
|
313
|
+
ZuluInstances->AddInstance(FTransform(FVector(0.0, 0.0, Index * 10.0)));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
FMCPHandlerRegistry Registry;
|
|
317
|
+
FLevelHandlers::RegisterHandlers(Registry);
|
|
318
|
+
const TSharedPtr<FJsonObject> Result = ExecuteStaticMeshUsage(Registry, 3, true, 2);
|
|
319
|
+
if (!TestTrue(TEXT("aggregation returns an object"), Result.IsValid()))
|
|
320
|
+
{
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
TestTrue(TEXT("aggregation succeeds"), Result->GetBoolField(TEXT("success")));
|
|
325
|
+
TestEqual(TEXT("the transient editor scope is reported"), Result->GetStringField(TEXT("world")), FString(TEXT("editor")));
|
|
326
|
+
TestTrue(TEXT("the scan explicitly reports loaded-only semantics"), Result->GetBoolField(TEXT("loadedOnly")));
|
|
327
|
+
TestEqual(TEXT("the requested row cap is reported"), static_cast<int32>(Result->GetNumberField(TEXT("maxResults"))), 3);
|
|
328
|
+
TestEqual(TEXT("the requested occurrence cap is reported"), static_cast<int32>(Result->GetNumberField(TEXT("maxOccurrences"))), 2);
|
|
329
|
+
TestEqual(TEXT("all fixture static-mesh components are scanned"), static_cast<int32>(Result->GetNumberField(TEXT("scannedStaticMeshComponents"))), 7);
|
|
330
|
+
TestEqual(TEXT("the null mesh component is diagnosed"), static_cast<int32>(Result->GetNumberField(TEXT("nullMeshComponents"))), 1);
|
|
331
|
+
TestEqual(TEXT("assigned components are counted separately"), static_cast<int32>(Result->GetNumberField(TEXT("totalComponentCount"))), 6);
|
|
332
|
+
TestEqual(TEXT("plain and instanced placements are counted exactly"), static_cast<int32>(Result->GetNumberField(TEXT("totalPlacementCount"))), 13);
|
|
333
|
+
TestEqual(TEXT("three unique meshes are aggregated"), static_cast<int32>(Result->GetNumberField(TEXT("totalUniqueMeshes"))), 3);
|
|
334
|
+
TestEqual(TEXT("all three mesh rows are returned"), static_cast<int32>(Result->GetNumberField(TEXT("returnedMeshCount"))), 3);
|
|
335
|
+
TestFalse(TEXT("the complete result is not truncated"), Result->GetBoolField(TEXT("truncated")));
|
|
336
|
+
TestEqual(TEXT("the global occurrence cap is honored"), static_cast<int32>(Result->GetNumberField(TEXT("returnedOccurrences"))), 2);
|
|
337
|
+
TestEqual(TEXT("all returned-row components contribute to the occurrence total"), static_cast<int32>(Result->GetNumberField(TEXT("totalOccurrences"))), 6);
|
|
338
|
+
TestTrue(TEXT("the occurrence sample reports truncation"), Result->GetBoolField(TEXT("occurrencesTruncated")));
|
|
339
|
+
|
|
340
|
+
const TArray<TSharedPtr<FJsonValue>>& Meshes = Result->GetArrayField(TEXT("meshes"));
|
|
341
|
+
TestEqual(TEXT("three mesh rows are present"), Meshes.Num(), 3);
|
|
342
|
+
if (Meshes.Num() == 3)
|
|
343
|
+
{
|
|
344
|
+
TestEqual(TEXT("highest placement row sorts first"), Meshes[0]->AsObject()->GetStringField(TEXT("meshPath")), BetaMesh->GetPathName());
|
|
345
|
+
TestEqual(TEXT("equal-placement rows tie-break by path"), Meshes[1]->AsObject()->GetStringField(TEXT("meshPath")), AlphaMesh->GetPathName());
|
|
346
|
+
TestEqual(TEXT("the remaining equal-placement row sorts last"), Meshes[2]->AsObject()->GetStringField(TEXT("meshPath")), ZuluMesh->GetPathName());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const TSharedPtr<FJsonObject> AlphaResult = FindMeshResult(Meshes, AlphaMesh->GetPathName());
|
|
350
|
+
const TSharedPtr<FJsonObject> BetaResult = FindMeshResult(Meshes, BetaMesh->GetPathName());
|
|
351
|
+
const TSharedPtr<FJsonObject> ZuluResult = FindMeshResult(Meshes, ZuluMesh->GetPathName());
|
|
352
|
+
if (TestTrue(TEXT("alpha aggregate exists"), AlphaResult.IsValid()))
|
|
353
|
+
{
|
|
354
|
+
TestEqual(TEXT("alpha actor count"), static_cast<int32>(AlphaResult->GetNumberField(TEXT("actorCount"))), 2);
|
|
355
|
+
TestEqual(TEXT("alpha component count"), static_cast<int32>(AlphaResult->GetNumberField(TEXT("componentCount"))), 3);
|
|
356
|
+
TestEqual(TEXT("alpha placement count"), static_cast<int32>(AlphaResult->GetNumberField(TEXT("placementCount"))), 4);
|
|
357
|
+
const TArray<TSharedPtr<FJsonValue>>& Occurrences = AlphaResult->GetArrayField(TEXT("occurrences"));
|
|
358
|
+
TestEqual(TEXT("alpha receives the two deterministic occurrence slots"), Occurrences.Num(), 2);
|
|
359
|
+
if (Occurrences.Num() == 2)
|
|
360
|
+
{
|
|
361
|
+
TestEqual(TEXT("first occurrence is path-ordered"), Occurrences[0]->AsObject()->GetStringField(TEXT("componentName")), FString(TEXT("CompAlphaAPlain")));
|
|
362
|
+
TestEqual(TEXT("second occurrence is path-ordered"), Occurrences[1]->AsObject()->GetStringField(TEXT("componentName")), FString(TEXT("CompAlphaBInstanced")));
|
|
363
|
+
}
|
|
364
|
+
TestTrue(TEXT("alpha reports its omitted occurrence"), AlphaResult->GetBoolField(TEXT("occurrencesTruncated")));
|
|
365
|
+
}
|
|
366
|
+
if (TestTrue(TEXT("beta aggregate exists"), BetaResult.IsValid()))
|
|
367
|
+
{
|
|
368
|
+
TestEqual(TEXT("beta actor count"), static_cast<int32>(BetaResult->GetNumberField(TEXT("actorCount"))), 1);
|
|
369
|
+
TestEqual(TEXT("beta component count"), static_cast<int32>(BetaResult->GetNumberField(TEXT("componentCount"))), 1);
|
|
370
|
+
TestEqual(TEXT("beta placement count"), static_cast<int32>(BetaResult->GetNumberField(TEXT("placementCount"))), 5);
|
|
371
|
+
}
|
|
372
|
+
if (TestTrue(TEXT("zulu aggregate exists"), ZuluResult.IsValid()))
|
|
373
|
+
{
|
|
374
|
+
TestEqual(TEXT("zero-instance components still count as components"), static_cast<int32>(ZuluResult->GetNumberField(TEXT("componentCount"))), 2);
|
|
375
|
+
TestEqual(TEXT("zero-instance components add no placements"), static_cast<int32>(ZuluResult->GetNumberField(TEXT("placementCount"))), 4);
|
|
376
|
+
TestEqual(TEXT("zulu actor count includes both referencing actors"), static_cast<int32>(ZuluResult->GetNumberField(TEXT("actorCount"))), 2);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const TSharedPtr<FJsonObject> BoundedResult = ExecuteStaticMeshUsage(Registry, 2, false, 256);
|
|
380
|
+
if (TestTrue(TEXT("bounded aggregation returns an object"), BoundedResult.IsValid()))
|
|
381
|
+
{
|
|
382
|
+
TestTrue(TEXT("bounded aggregation succeeds"), BoundedResult->GetBoolField(TEXT("success")));
|
|
383
|
+
TestEqual(TEXT("bounded aggregation reports the effective row cap"), static_cast<int32>(BoundedResult->GetNumberField(TEXT("maxResults"))), 2);
|
|
384
|
+
TestEqual(TEXT("bounded aggregation returns two rows"), static_cast<int32>(BoundedResult->GetNumberField(TEXT("returnedMeshCount"))), 2);
|
|
385
|
+
TestTrue(TEXT("bounded aggregation reports truncation"), BoundedResult->GetBoolField(TEXT("truncated")));
|
|
386
|
+
const TArray<TSharedPtr<FJsonValue>>& BoundedMeshes = BoundedResult->GetArrayField(TEXT("meshes"));
|
|
387
|
+
if (TestEqual(TEXT("bounded aggregation contains two rows"), BoundedMeshes.Num(), 2))
|
|
388
|
+
{
|
|
389
|
+
TestEqual(TEXT("top-N retains the highest placement row"), BoundedMeshes[0]->AsObject()->GetStringField(TEXT("meshPath")), BetaMesh->GetPathName());
|
|
390
|
+
TestEqual(TEXT("top-N deterministically retains the first tied row"), BoundedMeshes[1]->AsObject()->GetStringField(TEXT("meshPath")), AlphaMesh->GetPathName());
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const TSharedPtr<FJsonObject> ClampedResult = ExecuteStaticMeshUsage(Registry, 0, false, 999);
|
|
395
|
+
if (TestTrue(TEXT("out-of-range bounds return an object"), ClampedResult.IsValid()))
|
|
396
|
+
{
|
|
397
|
+
TestTrue(TEXT("clamped aggregation succeeds"), ClampedResult->GetBoolField(TEXT("success")));
|
|
398
|
+
TestEqual(TEXT("the row cap clamps to one"), static_cast<int32>(ClampedResult->GetNumberField(TEXT("maxResults"))), 1);
|
|
399
|
+
TestEqual(TEXT("the occurrence cap clamps to 256"), static_cast<int32>(ClampedResult->GetNumberField(TEXT("maxOccurrences"))), 256);
|
|
400
|
+
TestEqual(TEXT("the clamped result returns one row"), static_cast<int32>(ClampedResult->GetNumberField(TEXT("returnedMeshCount"))), 1);
|
|
401
|
+
TestTrue(TEXT("the clamped top-one result is truncated"), ClampedResult->GetBoolField(TEXT("truncated")));
|
|
402
|
+
const TArray<TSharedPtr<FJsonValue>>& ClampedMeshes = ClampedResult->GetArrayField(TEXT("meshes"));
|
|
403
|
+
if (TestEqual(TEXT("the clamped result contains one row"), ClampedMeshes.Num(), 1))
|
|
404
|
+
{
|
|
405
|
+
TestEqual(TEXT("the clamped top-one row is the highest-use mesh"), ClampedMeshes[0]->AsObject()->GetStringField(TEXT("meshPath")), BetaMesh->GetPathName());
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#endif
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Coverage for the world scope of the trace actions (#933).
|
|
2
|
+
//
|
|
3
|
+
// line_trace, bulk_line_trace and snap_actor_to_floor used to resolve the
|
|
4
|
+
// editor world unconditionally, so world='pie' during a PIE session answered
|
|
5
|
+
// from the pre-play editor world and reported it as the running game's
|
|
6
|
+
// geometry. Nothing about that answer looked wrong, which is what made it
|
|
7
|
+
// expensive. These assertions pin the two halves of the fix that hold without a
|
|
8
|
+
// running PIE session: the request is refused when the named world does not
|
|
9
|
+
// exist, and the response names the world that answered.
|
|
10
|
+
|
|
11
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
12
|
+
|
|
13
|
+
#include "Editor.h"
|
|
14
|
+
#include "HandlerRegistry.h"
|
|
15
|
+
#include "Handlers/LevelHandlers.h"
|
|
16
|
+
#include "Misc/AutomationTest.h"
|
|
17
|
+
|
|
18
|
+
namespace
|
|
19
|
+
{
|
|
20
|
+
TSharedPtr<FJsonObject> MCPTraceWorldTestRun(
|
|
21
|
+
FMCPHandlerRegistry& Registry,
|
|
22
|
+
const TCHAR* Method,
|
|
23
|
+
const TSharedPtr<FJsonObject>& Request)
|
|
24
|
+
{
|
|
25
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
|
|
26
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
27
|
+
{
|
|
28
|
+
return nullptr;
|
|
29
|
+
}
|
|
30
|
+
return Response->AsObject();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A downward trace request, so the shape is the same in every case below. */
|
|
34
|
+
TSharedPtr<FJsonObject> MCPTraceWorldTestRequest(const TCHAR* WorldScope)
|
|
35
|
+
{
|
|
36
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
37
|
+
|
|
38
|
+
TSharedPtr<FJsonObject> Start = MakeShared<FJsonObject>();
|
|
39
|
+
Start->SetNumberField(TEXT("x"), 0.0);
|
|
40
|
+
Start->SetNumberField(TEXT("y"), 0.0);
|
|
41
|
+
Start->SetNumberField(TEXT("z"), 10000.0);
|
|
42
|
+
Request->SetObjectField(TEXT("start"), Start);
|
|
43
|
+
|
|
44
|
+
TSharedPtr<FJsonObject> End = MakeShared<FJsonObject>();
|
|
45
|
+
End->SetNumberField(TEXT("x"), 0.0);
|
|
46
|
+
End->SetNumberField(TEXT("y"), 0.0);
|
|
47
|
+
End->SetNumberField(TEXT("z"), -10000.0);
|
|
48
|
+
Request->SetObjectField(TEXT("end"), End);
|
|
49
|
+
|
|
50
|
+
if (WorldScope) Request->SetStringField(TEXT("world"), WorldScope);
|
|
51
|
+
return Request;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
56
|
+
FLevelTraceWorldScopeTest,
|
|
57
|
+
"UE.MCP.Level.Trace.HonoursTheRequestedWorld",
|
|
58
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
59
|
+
|
|
60
|
+
bool FLevelTraceWorldScopeTest::RunTest(const FString& Parameters)
|
|
61
|
+
{
|
|
62
|
+
FMCPHandlerRegistry Registry;
|
|
63
|
+
FLevelHandlers::RegisterHandlers(Registry);
|
|
64
|
+
|
|
65
|
+
for (const TCHAR* Method : { TEXT("line_trace"), TEXT("bulk_line_trace"), TEXT("snap_actor_to_floor") })
|
|
66
|
+
{
|
|
67
|
+
TestTrue(FString::Printf(TEXT("%s is registered"), Method), Registry.HasHandler(Method));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// The default scope is the editor world, and the response says so. Without
|
|
71
|
+
// that field a caller comparing an editor trace against a PIE trace has no
|
|
72
|
+
// way to tell which one it is holding.
|
|
73
|
+
{
|
|
74
|
+
const TSharedPtr<FJsonObject> Response =
|
|
75
|
+
MCPTraceWorldTestRun(Registry, TEXT("line_trace"), MCPTraceWorldTestRequest(nullptr));
|
|
76
|
+
if (Response.IsValid() && Response->GetBoolField(TEXT("success")))
|
|
77
|
+
{
|
|
78
|
+
FString AnsweringWorld;
|
|
79
|
+
TestTrue(TEXT("the response names the world that answered"),
|
|
80
|
+
Response->TryGetStringField(TEXT("world"), AnsweringWorld));
|
|
81
|
+
TestEqual(TEXT("and the default scope is the editor world"), AnsweringWorld, FString(TEXT("editor")));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The regression itself: with no PIE session, world='pie' has no world to
|
|
86
|
+
// trace. It must be refused by name. Answering from the editor world is
|
|
87
|
+
// exactly the silent wrong answer #933 reported.
|
|
88
|
+
const bool bPIERunning = GEditor != nullptr && (GEditor->PlayWorld != nullptr || GEditor->bIsSimulatingInEditor);
|
|
89
|
+
if (!bPIERunning)
|
|
90
|
+
{
|
|
91
|
+
const TSharedPtr<FJsonObject> Response =
|
|
92
|
+
MCPTraceWorldTestRun(Registry, TEXT("line_trace"), MCPTraceWorldTestRequest(TEXT("pie")));
|
|
93
|
+
if (Response.IsValid())
|
|
94
|
+
{
|
|
95
|
+
TestFalse(TEXT("world='pie' without a PIE session does not silently trace the editor world"),
|
|
96
|
+
Response->GetBoolField(TEXT("success")));
|
|
97
|
+
TestTrue(TEXT("and the error names the scope that could not be resolved"),
|
|
98
|
+
Response->GetStringField(TEXT("error")).Contains(TEXT("pie")));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Same for the batch and for the floor snap, which resolve the world
|
|
102
|
+
// through the identical helper.
|
|
103
|
+
TSharedPtr<FJsonObject> BulkRequest = MakeShared<FJsonObject>();
|
|
104
|
+
TArray<TSharedPtr<FJsonValue>> Traces;
|
|
105
|
+
Traces.Add(MakeShared<FJsonValueObject>(MCPTraceWorldTestRequest(nullptr)));
|
|
106
|
+
BulkRequest->SetArrayField(TEXT("traces"), Traces);
|
|
107
|
+
BulkRequest->SetStringField(TEXT("world"), TEXT("pie"));
|
|
108
|
+
const TSharedPtr<FJsonObject> BulkResponse =
|
|
109
|
+
MCPTraceWorldTestRun(Registry, TEXT("bulk_line_trace"), BulkRequest);
|
|
110
|
+
if (BulkResponse.IsValid())
|
|
111
|
+
{
|
|
112
|
+
TestFalse(TEXT("bulk_line_trace refuses world='pie' without a PIE session"),
|
|
113
|
+
BulkResponse->GetBoolField(TEXT("success")));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
TSharedPtr<FJsonObject> SnapRequest = MakeShared<FJsonObject>();
|
|
117
|
+
SnapRequest->SetStringField(TEXT("actorLabel"), TEXT("UEMCP_NoSuchActor_TraceWorldScope"));
|
|
118
|
+
SnapRequest->SetStringField(TEXT("world"), TEXT("pie"));
|
|
119
|
+
const TSharedPtr<FJsonObject> SnapResponse =
|
|
120
|
+
MCPTraceWorldTestRun(Registry, TEXT("snap_actor_to_floor"), SnapRequest);
|
|
121
|
+
if (SnapResponse.IsValid())
|
|
122
|
+
{
|
|
123
|
+
TestFalse(TEXT("snap_actor_to_floor refuses world='pie' without a PIE session"),
|
|
124
|
+
SnapResponse->GetBoolField(TEXT("success")));
|
|
125
|
+
TestTrue(TEXT("and fails on the world rather than on the actor lookup"),
|
|
126
|
+
SnapResponse->GetStringField(TEXT("error")).Contains(TEXT("pie")));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#endif // WITH_DEV_AUTOMATION_TESTS
|