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
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp
CHANGED
|
@@ -61,17 +61,11 @@ TSharedPtr<FJsonValue> FWidgetHandlers::GetWidgetProperties(const TSharedPtr<FJs
|
|
|
61
61
|
FString WidgetName;
|
|
62
62
|
if (auto Err = RequireString(Params, TEXT("widgetName"), WidgetName)) return Err;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
UWidgetBlueprint* WidgetBP =
|
|
66
|
-
if (!WidgetBP)
|
|
67
|
-
{
|
|
68
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
69
|
-
}
|
|
64
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
65
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
66
|
+
if (!WidgetBP) return ResolveError;
|
|
70
67
|
|
|
71
|
-
if (!WidgetBP->WidgetTree)
|
|
72
|
-
{
|
|
73
|
-
return MCPError(TEXT("WidgetTree is null"));
|
|
74
|
-
}
|
|
68
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
75
69
|
|
|
76
70
|
// Find the widget
|
|
77
71
|
UWidget* FoundWidget = nullptr;
|
|
@@ -265,16 +259,10 @@ TSharedPtr<FJsonValue> FWidgetHandlers::GetWidgetFullProperties(const TSharedPtr
|
|
|
265
259
|
|
|
266
260
|
const bool bIncludeSubtree = OptionalBool(Params, TEXT("includeSubtree"), false);
|
|
267
261
|
|
|
268
|
-
|
|
269
|
-
UWidgetBlueprint* WidgetBP =
|
|
270
|
-
if (!WidgetBP)
|
|
271
|
-
|
|
272
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
273
|
-
}
|
|
274
|
-
if (!WidgetBP->WidgetTree)
|
|
275
|
-
{
|
|
276
|
-
return MCPError(TEXT("WidgetTree is null"));
|
|
277
|
-
}
|
|
262
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
263
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
264
|
+
if (!WidgetBP) return ResolveError;
|
|
265
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
278
266
|
|
|
279
267
|
UWidget* FoundWidget = nullptr;
|
|
280
268
|
WidgetBP->WidgetTree->ForEachWidget([&](UWidget* Widget)
|
|
@@ -369,11 +357,9 @@ TSharedPtr<FJsonValue> FWidgetHandlers::ListWidgetBindings(const TSharedPtr<FJso
|
|
|
369
357
|
const FString FilterWidget = OptionalString(Params, TEXT("filterWidgetName"));
|
|
370
358
|
const FString FilterProperty = OptionalString(Params, TEXT("filterProperty"));
|
|
371
359
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
376
|
-
}
|
|
360
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
361
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
362
|
+
if (!WidgetBP) return ResolveError;
|
|
377
363
|
|
|
378
364
|
TArray<TSharedPtr<FJsonValue>> BindingsArr;
|
|
379
365
|
for (const FDelegateEditorBinding& B : WidgetBP->Bindings)
|
|
@@ -411,11 +397,9 @@ TSharedPtr<FJsonValue> FWidgetHandlers::ClearWidgetBinding(const TSharedPtr<FJso
|
|
|
411
397
|
|
|
412
398
|
const FString PropertyName = OptionalString(Params, TEXT("propertyName"));
|
|
413
399
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
418
|
-
}
|
|
400
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
401
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
402
|
+
if (!WidgetBP) return ResolveError;
|
|
419
403
|
|
|
420
404
|
WidgetBP->Modify();
|
|
421
405
|
const int32 Removed = WidgetBP->Bindings.RemoveAll([&](const FDelegateEditorBinding& B)
|
|
@@ -456,17 +440,11 @@ TSharedPtr<FJsonValue> FWidgetHandlers::SetWidgetProperty(const TSharedPtr<FJson
|
|
|
456
440
|
FString PropertyValue;
|
|
457
441
|
if (auto Err = RequireStringAlt(Params, TEXT("propertyValue"), TEXT("value"), PropertyValue)) return Err;
|
|
458
442
|
|
|
459
|
-
|
|
460
|
-
UWidgetBlueprint* WidgetBP =
|
|
461
|
-
if (!WidgetBP)
|
|
462
|
-
{
|
|
463
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
464
|
-
}
|
|
443
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
444
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
445
|
+
if (!WidgetBP) return ResolveError;
|
|
465
446
|
|
|
466
|
-
if (!WidgetBP->WidgetTree)
|
|
467
|
-
{
|
|
468
|
-
return MCPError(TEXT("WidgetTree is null"));
|
|
469
|
-
}
|
|
447
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
470
448
|
|
|
471
449
|
// Find the widget
|
|
472
450
|
UWidget* FoundWidget = nullptr;
|
|
@@ -1047,12 +1025,9 @@ TSharedPtr<FJsonValue> FWidgetHandlers::ReadWidgetAnimations(const TSharedPtr<FJ
|
|
|
1047
1025
|
FString AssetPath;
|
|
1048
1026
|
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
1049
1027
|
|
|
1050
|
-
|
|
1051
|
-
UWidgetBlueprint* WidgetBP =
|
|
1052
|
-
if (!WidgetBP)
|
|
1053
|
-
{
|
|
1054
|
-
return MCPError(FString::Printf(TEXT("Failed to load WidgetBlueprint at '%s'"), *AssetPath));
|
|
1055
|
-
}
|
|
1028
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
1029
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
1030
|
+
if (!WidgetBP) return ResolveError;
|
|
1056
1031
|
|
|
1057
1032
|
TArray<TSharedPtr<FJsonValue>> AnimationsArray;
|
|
1058
1033
|
|
|
@@ -1178,8 +1153,10 @@ TSharedPtr<FJsonValue> FWidgetHandlers::SetWidgetStyle(const TSharedPtr<FJsonObj
|
|
|
1178
1153
|
TSharedPtr<FJsonValue> ValueField = Params->TryGetField(TEXT("value"));
|
|
1179
1154
|
if (!ValueField.IsValid()) return MCPError(TEXT("Missing 'value' (a JSON object/scalar for the style)"));
|
|
1180
1155
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1156
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
1157
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
1158
|
+
if (!WidgetBP) return ResolveError;
|
|
1159
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
1183
1160
|
|
|
1184
1161
|
UWidget* Widget = FindWidgetByName(WidgetBP, WidgetName);
|
|
1185
1162
|
if (!Widget) return MCPError(FString::Printf(TEXT("Widget not found: %s"), *WidgetName));
|
|
@@ -1217,8 +1194,10 @@ TSharedPtr<FJsonValue> FWidgetHandlers::BulkSetWidgetProperties(const TSharedPtr
|
|
|
1217
1194
|
return MCPError(TEXT("Missing 'properties' array ([{widgetName, propertyName, value}])"));
|
|
1218
1195
|
}
|
|
1219
1196
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1197
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
1198
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
1199
|
+
if (!WidgetBP) return ResolveError;
|
|
1200
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
1222
1201
|
|
|
1223
1202
|
TArray<TSharedPtr<FJsonValue>> Results;
|
|
1224
1203
|
int32 Applied = 0, Failed = 0;
|
|
@@ -1286,8 +1265,10 @@ TSharedPtr<FJsonValue> FWidgetHandlers::ReorderChild(const TSharedPtr<FJsonObjec
|
|
|
1286
1265
|
if (!Params->HasField(TEXT("index"))) return MCPError(TEXT("Missing 'index'"));
|
|
1287
1266
|
const int32 NewIndex = (int32)Params->GetNumberField(TEXT("index"));
|
|
1288
1267
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1268
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
1269
|
+
UWidgetBlueprint* WidgetBP = MCPWidget::ResolveWidgetBlueprintOrError(AssetPath, ResolveError);
|
|
1270
|
+
if (!WidgetBP) return ResolveError;
|
|
1271
|
+
if (!WidgetBP->WidgetTree) return MCPWidget::MissingWidgetTreeError(AssetPath);
|
|
1291
1272
|
UWidget* Widget = FindWidgetByName(WidgetBP, WidgetName);
|
|
1292
1273
|
if (!Widget) return MCPError(FString::Printf(TEXT("Widget not found: %s"), *WidgetName));
|
|
1293
1274
|
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// Coverage for the shared actor selector (#983).
|
|
2
|
+
//
|
|
3
|
+
// The reported harm was a level holding several actors labelled
|
|
4
|
+
// BP_SnappyRoad2, a write aimed at the one the user had selected, and the edit
|
|
5
|
+
// landing on a road at the other end of the map with a success response. So
|
|
6
|
+
// the case that matters here is two actors sharing a label: the call must
|
|
7
|
+
// refuse, and it must name both candidates with their actorPath so the retry
|
|
8
|
+
// is a copy of one field.
|
|
9
|
+
//
|
|
10
|
+
// The two actors are transient verification actors (level(spawn_transient_actor)),
|
|
11
|
+
// which cannot be saved into the map and are destroyed before the test returns.
|
|
12
|
+
// run_automation_tests dispatches every EditorContext test against whatever
|
|
13
|
+
// project the bridge is attached to, so nothing here may persist anything.
|
|
14
|
+
|
|
15
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
16
|
+
|
|
17
|
+
#include "HandlerRegistry.h"
|
|
18
|
+
#include "HandlerUtils.h"
|
|
19
|
+
#include "Handlers/LevelHandlers.h"
|
|
20
|
+
#include "Engine/World.h"
|
|
21
|
+
#include "EngineUtils.h"
|
|
22
|
+
#include "GameFramework/Actor.h"
|
|
23
|
+
#include "Misc/AutomationTest.h"
|
|
24
|
+
|
|
25
|
+
namespace
|
|
26
|
+
{
|
|
27
|
+
const TCHAR* MCPSelectorTestLabel = TEXT("UEMCP_AmbiguousSelectorTest");
|
|
28
|
+
|
|
29
|
+
TSharedPtr<FJsonObject> MCPSelectorRun(
|
|
30
|
+
FMCPHandlerRegistry& Registry,
|
|
31
|
+
const TCHAR* Method,
|
|
32
|
+
const TSharedPtr<FJsonObject>& Request)
|
|
33
|
+
{
|
|
34
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
|
|
35
|
+
if (!Response.IsValid() || Response->Type != EJson::Object)
|
|
36
|
+
{
|
|
37
|
+
return nullptr;
|
|
38
|
+
}
|
|
39
|
+
return Response->AsObject();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Spawn one transient StaticMeshActor carrying the shared test label.
|
|
43
|
+
* Returns its object path, or an empty string when there is no world to
|
|
44
|
+
* spawn into (a context this test must skip rather than fail). */
|
|
45
|
+
FString MCPSelectorSpawn(FMCPHandlerRegistry& Registry)
|
|
46
|
+
{
|
|
47
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
48
|
+
Request->SetStringField(TEXT("actorClass"), TEXT("StaticMeshActor"));
|
|
49
|
+
Request->SetStringField(TEXT("label"), MCPSelectorTestLabel);
|
|
50
|
+
Request->SetBoolField(TEXT("hideFromOutliner"), true);
|
|
51
|
+
const TSharedPtr<FJsonObject> Response =
|
|
52
|
+
MCPSelectorRun(Registry, TEXT("spawn_transient_actor"), Request);
|
|
53
|
+
if (!Response.IsValid() || !Response->GetBoolField(TEXT("success")))
|
|
54
|
+
{
|
|
55
|
+
return FString();
|
|
56
|
+
}
|
|
57
|
+
const TSharedPtr<FJsonObject> Actor = Response->GetObjectField(TEXT("actor"));
|
|
58
|
+
return Actor.IsValid() ? Actor->GetStringField(TEXT("actorPath")) : FString();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void MCPSelectorDestroy(FMCPHandlerRegistry& Registry, const FString& ActorPath)
|
|
62
|
+
{
|
|
63
|
+
if (ActorPath.IsEmpty()) return;
|
|
64
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
65
|
+
Request->SetStringField(TEXT("actorPath"), ActorPath);
|
|
66
|
+
MCPSelectorRun(Registry, TEXT("destroy_transient_actor"), Request);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A boolean field that may be absent, without a JSON warning for the miss. */
|
|
70
|
+
bool MCPSelectorFlag(const TSharedPtr<FJsonObject>& Object, const TCHAR* Field)
|
|
71
|
+
{
|
|
72
|
+
bool bValue = false;
|
|
73
|
+
return Object.IsValid() && Object->TryGetBoolField(Field, bValue) && bValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Every actorPath in an ambiguity refusal's candidates array. */
|
|
77
|
+
TArray<FString> MCPSelectorCandidatePaths(const TSharedPtr<FJsonObject>& Response)
|
|
78
|
+
{
|
|
79
|
+
TArray<FString> Paths;
|
|
80
|
+
if (!Response.IsValid()) return Paths;
|
|
81
|
+
const TArray<TSharedPtr<FJsonValue>>* Candidates = nullptr;
|
|
82
|
+
if (!Response->TryGetArrayField(TEXT("candidates"), Candidates) || !Candidates) return Paths;
|
|
83
|
+
for (const TSharedPtr<FJsonValue>& Entry : *Candidates)
|
|
84
|
+
{
|
|
85
|
+
if (!Entry.IsValid() || Entry->Type != EJson::Object) continue;
|
|
86
|
+
FString Path;
|
|
87
|
+
if (Entry->AsObject()->TryGetStringField(TEXT("actorPath"), Path)) Paths.Add(Path);
|
|
88
|
+
}
|
|
89
|
+
return Paths;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
94
|
+
FMCPActorSelectorAmbiguityTest,
|
|
95
|
+
"UE.MCP.Level.ActorSelector.DuplicateLabelIsRefused",
|
|
96
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
97
|
+
|
|
98
|
+
bool FMCPActorSelectorAmbiguityTest::RunTest(const FString& Parameters)
|
|
99
|
+
{
|
|
100
|
+
FMCPHandlerRegistry Registry;
|
|
101
|
+
FLevelHandlers::RegisterHandlers(Registry);
|
|
102
|
+
|
|
103
|
+
UWorld* World = GetEditorWorld();
|
|
104
|
+
if (!World)
|
|
105
|
+
{
|
|
106
|
+
// No editor world in this context, and this test may not open a map.
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const FString FirstPath = MCPSelectorSpawn(Registry);
|
|
111
|
+
if (FirstPath.IsEmpty())
|
|
112
|
+
{
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
const FString SecondPath = MCPSelectorSpawn(Registry);
|
|
116
|
+
if (SecondPath.IsEmpty())
|
|
117
|
+
{
|
|
118
|
+
MCPSelectorDestroy(Registry, FirstPath);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
TestNotEqual(TEXT("the two same-labelled actors have different object paths"), FirstPath, SecondPath);
|
|
123
|
+
|
|
124
|
+
// The search itself sees both, which is the fact every refusal rests on.
|
|
125
|
+
{
|
|
126
|
+
TArray<AActor*> Matches;
|
|
127
|
+
MCPCollectActorsByToken(World, MCPSelectorTestLabel, EMCPActorMatch::Label, Matches);
|
|
128
|
+
TestEqual(TEXT("the label names both actors"), Matches.Num(), 2);
|
|
129
|
+
|
|
130
|
+
// Sorted by object path, so two runs produce the same candidate order
|
|
131
|
+
// and the error is reproducible rather than iterator-dependent.
|
|
132
|
+
if (Matches.Num() == 2)
|
|
133
|
+
{
|
|
134
|
+
TestTrue(TEXT("matches come back in a stable path order"),
|
|
135
|
+
Matches[0]->GetPathName().Compare(Matches[1]->GetPathName()) < 0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// The path selector resolves to exactly one, with no label involved.
|
|
140
|
+
{
|
|
141
|
+
AActor* ByPath = MCPFindActorByPath(World, FirstPath);
|
|
142
|
+
TestTrue(TEXT("the path resolves"), ByPath != nullptr);
|
|
143
|
+
if (ByPath) TestEqual(TEXT("and to the actor it names"), ByPath->GetPathName(), FirstPath);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The reported case: a label matching two actors is refused, and the
|
|
147
|
+
// refusal names both candidates with their actorPath.
|
|
148
|
+
{
|
|
149
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
150
|
+
Request->SetStringField(TEXT("actorLabel"), MCPSelectorTestLabel);
|
|
151
|
+
const TSharedPtr<FJsonObject> Response =
|
|
152
|
+
MCPSelectorRun(Registry, TEXT("get_actor_details"), Request);
|
|
153
|
+
TestTrue(TEXT("the ambiguous call answers"), Response.IsValid());
|
|
154
|
+
if (Response.IsValid())
|
|
155
|
+
{
|
|
156
|
+
TestFalse(TEXT("an ambiguous label fails rather than picking one"),
|
|
157
|
+
Response->GetBoolField(TEXT("success")));
|
|
158
|
+
TestTrue(TEXT("and says it was ambiguous"),
|
|
159
|
+
MCPSelectorFlag(Response, TEXT("ambiguous")));
|
|
160
|
+
TestEqual(TEXT("and reports how many actors matched"),
|
|
161
|
+
static_cast<int32>(Response->GetNumberField(TEXT("matchCount"))), 2);
|
|
162
|
+
TestEqual(TEXT("and names the selector that was ambiguous"),
|
|
163
|
+
Response->GetStringField(TEXT("selector")), FString(TEXT("actorLabel")));
|
|
164
|
+
TestTrue(TEXT("and the message points at actorPath for the retry"),
|
|
165
|
+
Response->GetStringField(TEXT("error")).Contains(TEXT("actorPath")));
|
|
166
|
+
|
|
167
|
+
const TArray<FString> Paths = MCPSelectorCandidatePaths(Response);
|
|
168
|
+
TestEqual(TEXT("both candidates are listed"), Paths.Num(), 2);
|
|
169
|
+
TestTrue(TEXT("the first actor is named"), Paths.Contains(FirstPath));
|
|
170
|
+
TestTrue(TEXT("the second actor is named"), Paths.Contains(SecondPath));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// A write refuses on the same terms. This is the half that mattered: the
|
|
175
|
+
// report was a silent edit, not a silent read.
|
|
176
|
+
{
|
|
177
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
178
|
+
Request->SetStringField(TEXT("actorLabel"), MCPSelectorTestLabel);
|
|
179
|
+
Request->SetStringField(TEXT("propertyName"), TEXT("bHidden"));
|
|
180
|
+
Request->SetBoolField(TEXT("value"), true);
|
|
181
|
+
const TSharedPtr<FJsonObject> Response =
|
|
182
|
+
MCPSelectorRun(Registry, TEXT("set_actor_property"), Request);
|
|
183
|
+
if (Response.IsValid())
|
|
184
|
+
{
|
|
185
|
+
TestFalse(TEXT("an ambiguous write is refused"), Response->GetBoolField(TEXT("success")));
|
|
186
|
+
TestTrue(TEXT("and is refused for ambiguity, not for anything else"),
|
|
187
|
+
MCPSelectorFlag(Response, TEXT("ambiguous")));
|
|
188
|
+
TestEqual(TEXT("naming both candidates"), MCPSelectorCandidatePaths(Response).Num(), 2);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// delete_actor is idempotent on a miss, but "already deleted" would be a
|
|
193
|
+
// lie while two namesakes are still standing.
|
|
194
|
+
{
|
|
195
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
196
|
+
Request->SetStringField(TEXT("actorLabel"), MCPSelectorTestLabel);
|
|
197
|
+
const TSharedPtr<FJsonObject> Response =
|
|
198
|
+
MCPSelectorRun(Registry, TEXT("delete_actor"), Request);
|
|
199
|
+
if (Response.IsValid())
|
|
200
|
+
{
|
|
201
|
+
TestFalse(TEXT("an ambiguous delete is refused"), Response->GetBoolField(TEXT("success")));
|
|
202
|
+
TestTrue(TEXT("and does not claim the actor was already deleted"),
|
|
203
|
+
MCPSelectorFlag(Response, TEXT("ambiguous")));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// The unambiguous selector gets through, and answers about the actor it
|
|
208
|
+
// names rather than its namesake.
|
|
209
|
+
{
|
|
210
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
211
|
+
Request->SetStringField(TEXT("actorPath"), SecondPath);
|
|
212
|
+
const TSharedPtr<FJsonObject> Response =
|
|
213
|
+
MCPSelectorRun(Registry, TEXT("get_actor_details"), Request);
|
|
214
|
+
TestTrue(TEXT("actorPath alone resolves"),
|
|
215
|
+
Response.IsValid() && Response->GetBoolField(TEXT("success")));
|
|
216
|
+
if (Response.IsValid() && Response->GetBoolField(TEXT("success")))
|
|
217
|
+
{
|
|
218
|
+
TestEqual(TEXT("and answers about that actor"),
|
|
219
|
+
Response->GetStringField(TEXT("path")), SecondPath);
|
|
220
|
+
TestEqual(TEXT("and returns actorPath so the round trip closes"),
|
|
221
|
+
Response->GetStringField(TEXT("actorPath")), SecondPath);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Both given: the path wins, because it is the one that cannot be wrong.
|
|
226
|
+
{
|
|
227
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
228
|
+
Request->SetStringField(TEXT("actorLabel"), MCPSelectorTestLabel);
|
|
229
|
+
Request->SetStringField(TEXT("actorPath"), FirstPath);
|
|
230
|
+
const TSharedPtr<FJsonObject> Response =
|
|
231
|
+
MCPSelectorRun(Registry, TEXT("get_actor_details"), Request);
|
|
232
|
+
TestTrue(TEXT("a path alongside an ambiguous label still resolves"),
|
|
233
|
+
Response.IsValid() && Response->GetBoolField(TEXT("success")));
|
|
234
|
+
if (Response.IsValid() && Response->GetBoolField(TEXT("success")))
|
|
235
|
+
{
|
|
236
|
+
TestEqual(TEXT("and the path decides"), Response->GetStringField(TEXT("actorPath")), FirstPath);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// A path that names nothing is an error about the path. Falling through to
|
|
241
|
+
// the label would demote a precise miss to a fuzzy hit, which is the same
|
|
242
|
+
// class of bug this issue is about.
|
|
243
|
+
{
|
|
244
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
245
|
+
Request->SetStringField(TEXT("actorLabel"), MCPSelectorTestLabel);
|
|
246
|
+
Request->SetStringField(TEXT("actorPath"), TEXT("/Game/NoSuchMap.NoSuchMap:PersistentLevel.NoSuchActor_0"));
|
|
247
|
+
const TSharedPtr<FJsonObject> Response =
|
|
248
|
+
MCPSelectorRun(Registry, TEXT("get_actor_details"), Request);
|
|
249
|
+
if (Response.IsValid())
|
|
250
|
+
{
|
|
251
|
+
TestFalse(TEXT("an unresolvable actorPath fails"), Response->GetBoolField(TEXT("success")));
|
|
252
|
+
TestTrue(TEXT("and the message names the path, not the label"),
|
|
253
|
+
Response->GetStringField(TEXT("error")).Contains(TEXT("NoSuchActor_0")));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
MCPSelectorDestroy(Registry, FirstPath);
|
|
258
|
+
MCPSelectorDestroy(Registry, SecondPath);
|
|
259
|
+
|
|
260
|
+
// The cleanup really happened: a later test must not inherit two actors
|
|
261
|
+
// sharing a label.
|
|
262
|
+
{
|
|
263
|
+
TArray<AActor*> Remaining;
|
|
264
|
+
MCPCollectActorsByToken(World, MCPSelectorTestLabel, EMCPActorMatch::Label, Remaining);
|
|
265
|
+
TestEqual(TEXT("both verification actors were destroyed"), Remaining.Num(), 0);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
272
|
+
FMCPActorSelectorContractTest,
|
|
273
|
+
"UE.MCP.Level.ActorSelector.Contract",
|
|
274
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
275
|
+
|
|
276
|
+
bool FMCPActorSelectorContractTest::RunTest(const FString& Parameters)
|
|
277
|
+
{
|
|
278
|
+
FMCPHandlerRegistry Registry;
|
|
279
|
+
FLevelHandlers::RegisterHandlers(Registry);
|
|
280
|
+
|
|
281
|
+
// Neither selector is a parameter error that names both, so a caller who
|
|
282
|
+
// only knows actorLabel learns that actorPath exists.
|
|
283
|
+
{
|
|
284
|
+
const TSharedPtr<FJsonValue> Response =
|
|
285
|
+
Registry.ExecuteHandler(TEXT("get_actor_details"), MakeShared<FJsonObject>());
|
|
286
|
+
if (Response.IsValid() && Response->Type == EJson::Object)
|
|
287
|
+
{
|
|
288
|
+
const TSharedPtr<FJsonObject> Object = Response->AsObject();
|
|
289
|
+
TestFalse(TEXT("no selector fails"), Object->GetBoolField(TEXT("success")));
|
|
290
|
+
const FString Error = Object->GetStringField(TEXT("error"));
|
|
291
|
+
TestTrue(TEXT("and names actorLabel"), Error.Contains(TEXT("actorLabel")));
|
|
292
|
+
TestTrue(TEXT("and names actorPath"), Error.Contains(TEXT("actorPath")));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// The refusal is a shape a caller can branch on without parsing prose.
|
|
297
|
+
{
|
|
298
|
+
UWorld* World = GetEditorWorld();
|
|
299
|
+
TArray<AActor*> Candidates;
|
|
300
|
+
if (World)
|
|
301
|
+
{
|
|
302
|
+
for (TActorIterator<AActor> It(World); It && Candidates.Num() < 2; ++It)
|
|
303
|
+
{
|
|
304
|
+
if (IsValid(*It)) Candidates.Add(*It);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (Candidates.Num() == 2)
|
|
308
|
+
{
|
|
309
|
+
const TSharedPtr<FJsonValue> Error = MCPAmbiguousActorError(
|
|
310
|
+
TEXT("SharedLabel"), TEXT("actorLabel"), TEXT("actorPath"), TEXT("editor label"), Candidates);
|
|
311
|
+
TestTrue(TEXT("the refusal is an object"), Error.IsValid() && Error->Type == EJson::Object);
|
|
312
|
+
if (Error.IsValid() && Error->Type == EJson::Object)
|
|
313
|
+
{
|
|
314
|
+
const TSharedPtr<FJsonObject> Object = Error->AsObject();
|
|
315
|
+
TestFalse(TEXT("it is a failure"), Object->GetBoolField(TEXT("success")));
|
|
316
|
+
TestTrue(TEXT("flagged ambiguous"), MCPSelectorFlag(Object, TEXT("ambiguous")));
|
|
317
|
+
TestTrue(TEXT("and MCPIsAmbiguousActorError agrees"), MCPIsAmbiguousActorError(Error));
|
|
318
|
+
TestEqual(TEXT("echoing the value that was ambiguous"),
|
|
319
|
+
Object->GetStringField(TEXT("selectorValue")), FString(TEXT("SharedLabel")));
|
|
320
|
+
TestEqual(TEXT("with a candidate row per actor"),
|
|
321
|
+
MCPSelectorCandidatePaths(Object).Num(), 2);
|
|
322
|
+
|
|
323
|
+
// Each row carries enough to tell two namesakes apart without a
|
|
324
|
+
// follow-up call: class, folder and location, not just a path.
|
|
325
|
+
const TArray<TSharedPtr<FJsonValue>>* Rows = nullptr;
|
|
326
|
+
if (Object->TryGetArrayField(TEXT("candidates"), Rows) && Rows && Rows->Num() > 0)
|
|
327
|
+
{
|
|
328
|
+
const TSharedPtr<FJsonObject> Row = (*Rows)[0]->AsObject();
|
|
329
|
+
TestTrue(TEXT("a candidate row carries actorLabel"), Row->HasField(TEXT("actorLabel")));
|
|
330
|
+
TestTrue(TEXT("a candidate row carries actorClass"), Row->HasField(TEXT("actorClass")));
|
|
331
|
+
TestTrue(TEXT("a candidate row carries location"), Row->HasField(TEXT("location")));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// A plain error is not mistaken for an ambiguity refusal.
|
|
338
|
+
TestFalse(TEXT("a plain error is not ambiguous"), MCPIsAmbiguousActorError(MCPError(TEXT("nope"))));
|
|
339
|
+
TestFalse(TEXT("an unset error is not ambiguous"), MCPIsAmbiguousActorError(TSharedPtr<FJsonValue>()));
|
|
340
|
+
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
#endif // WITH_DEV_AUTOMATION_TESTS
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#if WITH_DEV_AUTOMATION_TESTS && (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
|
|
2
|
+
|
|
3
|
+
#include "Animation/AnimData/IAnimationDataController.h"
|
|
4
|
+
#include "Animation/AnimSequence.h"
|
|
5
|
+
#include "Animation/Skeleton.h"
|
|
6
|
+
#include "Misc/AutomationTest.h"
|
|
7
|
+
#include "ReferenceSkeleton.h"
|
|
8
|
+
#include "Sections/MovieSceneSkeletalAnimationSection.h"
|
|
9
|
+
|
|
10
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
11
|
+
FAnimationControlRigRawTimelineRateScaleTest,
|
|
12
|
+
"UE.MCP.Animation.ControlRig.RawTimelineRateScale",
|
|
13
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
14
|
+
|
|
15
|
+
bool FAnimationControlRigRawTimelineRateScaleTest::RunTest(const FString& Parameters)
|
|
16
|
+
{
|
|
17
|
+
const FFrameRate DisplayRate(25, 1);
|
|
18
|
+
USkeleton* Skeleton = NewObject<USkeleton>(GetTransientPackage());
|
|
19
|
+
{
|
|
20
|
+
FReferenceSkeletonModifier Modifier(Skeleton);
|
|
21
|
+
Modifier.Add(FMeshBoneInfo(TEXT("root"), TEXT("root"), INDEX_NONE), FTransform::Identity);
|
|
22
|
+
}
|
|
23
|
+
UAnimSequence* SourceAnimation = NewObject<UAnimSequence>(GetTransientPackage());
|
|
24
|
+
SourceAnimation->SetSkeleton(Skeleton);
|
|
25
|
+
IAnimationDataController& Controller = SourceAnimation->GetController();
|
|
26
|
+
Controller.InitializeModel();
|
|
27
|
+
Controller.OpenBracket(FText::FromString(TEXT("Build raw-timeline test animation")), false);
|
|
28
|
+
Controller.SetFrameRate(DisplayRate, false);
|
|
29
|
+
Controller.SetNumberOfFrames(FFrameNumber(40), false);
|
|
30
|
+
Controller.CloseBracket(false);
|
|
31
|
+
SourceAnimation->RateScale = 3.06608796f;
|
|
32
|
+
|
|
33
|
+
UMovieSceneSkeletalAnimationSection* Section =
|
|
34
|
+
NewObject<UMovieSceneSkeletalAnimationSection>(GetTransientPackage());
|
|
35
|
+
Section->Params.Animation = SourceAnimation;
|
|
36
|
+
Section->Params.PlayRate = 1.0f / SourceAnimation->RateScale;
|
|
37
|
+
Section->SetRange(TRange<FFrameNumber>(FFrameNumber(0), FFrameNumber(41)));
|
|
38
|
+
|
|
39
|
+
double PreviousTime = -1.0;
|
|
40
|
+
for (int32 Frame = 0; Frame <= 40; ++Frame)
|
|
41
|
+
{
|
|
42
|
+
const double ExpectedTime = static_cast<double>(Frame) / DisplayRate.AsDecimal();
|
|
43
|
+
const double MappedTime = Section->MapTimeToAnimation(FFrameTime(Frame), DisplayRate);
|
|
44
|
+
TestTrue(
|
|
45
|
+
*FString::Printf(TEXT("frame %d maps to raw source time"), Frame),
|
|
46
|
+
FMath::IsNearlyEqual(MappedTime, ExpectedTime, 0.0001));
|
|
47
|
+
TestTrue(
|
|
48
|
+
*FString::Printf(TEXT("frame %d does not loop backwards"), Frame),
|
|
49
|
+
MappedTime >= PreviousTime);
|
|
50
|
+
PreviousTime = MappedTime;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TestTrue(
|
|
54
|
+
TEXT("the final frame reaches the source end once"),
|
|
55
|
+
FMath::IsNearlyEqual(PreviousTime, SourceAnimation->GetPlayLength(), 0.0001));
|
|
56
|
+
TestEqual(TEXT("the source RateScale is unchanged"), SourceAnimation->RateScale, 3.06608796f);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#endif
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Coverage for the evaluated-pose reads - animation(sample_pose),
|
|
2
|
+
// animation(get_live_bone_transforms) and animation(measure_natural_speed) -
|
|
3
|
+
// that is safe to run anywhere.
|
|
4
|
+
//
|
|
5
|
+
// run_automation_tests dispatches every EditorContext/EngineFilter test in the
|
|
6
|
+
// process when it is called without a filter, against whatever project the
|
|
7
|
+
// bridge is attached to. All three are reads, and none of the cases below name
|
|
8
|
+
// a real asset or actor, so the suite holds in any project. Evaluating an actual
|
|
9
|
+
// clip is exercised by the smoke suite against the dedicated test project.
|
|
10
|
+
|
|
11
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
12
|
+
|
|
13
|
+
#include "HandlerRegistry.h"
|
|
14
|
+
#include "Handlers/AnimationHandlers.h"
|
|
15
|
+
#include "Misc/AutomationTest.h"
|
|
16
|
+
|
|
17
|
+
namespace
|
|
18
|
+
{
|
|
19
|
+
TSharedPtr<FJsonObject> MakePoseResponseObject(const TSharedPtr<FJsonValue>& Response)
|
|
20
|
+
{
|
|
21
|
+
return (Response.IsValid() && Response->Type == EJson::Object) ? Response->AsObject() : nullptr;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
26
|
+
FAnimationPoseRegistrationTest,
|
|
27
|
+
"UE.MCP.Animation.Pose.RegistrationAndValidation",
|
|
28
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
29
|
+
|
|
30
|
+
bool FAnimationPoseRegistrationTest::RunTest(const FString& Parameters)
|
|
31
|
+
{
|
|
32
|
+
FMCPHandlerRegistry Registry;
|
|
33
|
+
FAnimationHandlers::RegisterHandlers(Registry);
|
|
34
|
+
TestTrue(TEXT("sample_pose is registered"), Registry.HasHandler(TEXT("sample_pose")));
|
|
35
|
+
TestTrue(TEXT("get_live_bone_transforms is registered"), Registry.HasHandler(TEXT("get_live_bone_transforms")));
|
|
36
|
+
TestTrue(TEXT("measure_natural_speed is registered"), Registry.HasHandler(TEXT("measure_natural_speed")));
|
|
37
|
+
|
|
38
|
+
// sample_pose without an asset names the parameter it wants.
|
|
39
|
+
{
|
|
40
|
+
const TSharedPtr<FJsonObject> Missing = MakePoseResponseObject(
|
|
41
|
+
Registry.ExecuteHandler(TEXT("sample_pose"), MakeShared<FJsonObject>()));
|
|
42
|
+
TestTrue(TEXT("sample_pose missing assetPath returns an object"), Missing.IsValid());
|
|
43
|
+
if (Missing.IsValid())
|
|
44
|
+
{
|
|
45
|
+
TestFalse(TEXT("sample_pose missing assetPath is unsuccessful"), Missing->GetBoolField(TEXT("success")));
|
|
46
|
+
TestTrue(TEXT("sample_pose missing assetPath names assetPath"),
|
|
47
|
+
Missing->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// An unresolvable asset reports the path rather than dereferencing null.
|
|
52
|
+
{
|
|
53
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
54
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_SamplePoseTest"));
|
|
55
|
+
const TSharedPtr<FJsonObject> NotFound = MakePoseResponseObject(
|
|
56
|
+
Registry.ExecuteHandler(TEXT("sample_pose"), Request));
|
|
57
|
+
TestTrue(TEXT("sample_pose unknown asset returns an object"), NotFound.IsValid());
|
|
58
|
+
if (NotFound.IsValid())
|
|
59
|
+
{
|
|
60
|
+
TestFalse(TEXT("sample_pose unknown asset is unsuccessful"), NotFound->GetBoolField(TEXT("success")));
|
|
61
|
+
TestTrue(TEXT("sample_pose unknown asset echoes the path"),
|
|
62
|
+
NotFound->GetStringField(TEXT("error")).Contains(TEXT("DoesNotExist_SamplePoseTest")));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// measure_natural_speed validates its foot set before it touches an asset,
|
|
67
|
+
// because a speed measured without knowing which bones are feet is a number
|
|
68
|
+
// with no meaning rather than an approximation.
|
|
69
|
+
{
|
|
70
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
71
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_NaturalSpeedTest"));
|
|
72
|
+
const TSharedPtr<FJsonObject> Missing = MakePoseResponseObject(
|
|
73
|
+
Registry.ExecuteHandler(TEXT("measure_natural_speed"), Request));
|
|
74
|
+
TestTrue(TEXT("measure_natural_speed missing footBones returns an object"), Missing.IsValid());
|
|
75
|
+
if (Missing.IsValid())
|
|
76
|
+
{
|
|
77
|
+
TestFalse(TEXT("measure_natural_speed missing footBones is unsuccessful"), Missing->GetBoolField(TEXT("success")));
|
|
78
|
+
TestTrue(TEXT("measure_natural_speed missing footBones names footBones"),
|
|
79
|
+
Missing->GetStringField(TEXT("error")).Contains(TEXT("footBones")));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// get_live_bone_transforms needs an actor, and rejects a space it cannot
|
|
84
|
+
// serve before it goes looking for one.
|
|
85
|
+
{
|
|
86
|
+
const TSharedPtr<FJsonObject> Missing = MakePoseResponseObject(
|
|
87
|
+
Registry.ExecuteHandler(TEXT("get_live_bone_transforms"), MakeShared<FJsonObject>()));
|
|
88
|
+
TestTrue(TEXT("get_live_bone_transforms missing actorLabel returns an object"), Missing.IsValid());
|
|
89
|
+
if (Missing.IsValid())
|
|
90
|
+
{
|
|
91
|
+
TestFalse(TEXT("get_live_bone_transforms missing actorLabel is unsuccessful"), Missing->GetBoolField(TEXT("success")));
|
|
92
|
+
TestTrue(TEXT("get_live_bone_transforms missing actorLabel names actorLabel"),
|
|
93
|
+
Missing->GetStringField(TEXT("error")).Contains(TEXT("actorLabel")));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
{
|
|
97
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
98
|
+
Request->SetStringField(TEXT("actorLabel"), TEXT("UEMCP_DoesNotExist_LiveBoneTest"));
|
|
99
|
+
Request->SetStringField(TEXT("space"), TEXT("skeleton"));
|
|
100
|
+
const TSharedPtr<FJsonObject> Rejected = MakePoseResponseObject(
|
|
101
|
+
Registry.ExecuteHandler(TEXT("get_live_bone_transforms"), Request));
|
|
102
|
+
TestTrue(TEXT("get_live_bone_transforms bad space returns an object"), Rejected.IsValid());
|
|
103
|
+
if (Rejected.IsValid())
|
|
104
|
+
{
|
|
105
|
+
TestFalse(TEXT("get_live_bone_transforms bad space is unsuccessful"), Rejected->GetBoolField(TEXT("success")));
|
|
106
|
+
const FString Error = Rejected->GetStringField(TEXT("error"));
|
|
107
|
+
TestTrue(TEXT("get_live_bone_transforms bad space lists world"), Error.Contains(TEXT("world")));
|
|
108
|
+
TestTrue(TEXT("get_live_bone_transforms bad space lists component"), Error.Contains(TEXT("component")));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#endif // WITH_DEV_AUTOMATION_TESTS
|