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,243 @@
|
|
|
1
|
+
// Regression coverage for #957 and #913.
|
|
2
|
+
//
|
|
3
|
+
// A short package path and its object path form name the same asset, and
|
|
4
|
+
// asset(search) reports the short one. Several actions accepted only the object
|
|
5
|
+
// path, so an asset that existed and had just been reported by search answered
|
|
6
|
+
// "Asset not found", and the sentence was the same one a genuinely missing path
|
|
7
|
+
// produced. These tests pin the two halves of the fix: every path form derives
|
|
8
|
+
// the same object path, and a miss carries enough structure to tell a path-shape
|
|
9
|
+
// bug from a missing asset.
|
|
10
|
+
//
|
|
11
|
+
// The test writes nowhere near the attached project. run_automation_tests
|
|
12
|
+
// dispatches every EditorContext/EngineFilter test in the process against
|
|
13
|
+
// whatever project the bridge happens to be attached to, so this registers its
|
|
14
|
+
// own mount point over a fresh directory in the system temp area, does the whole
|
|
15
|
+
// exercise there, then unmounts and deletes it.
|
|
16
|
+
|
|
17
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
18
|
+
|
|
19
|
+
#include "HandlerUtils.h"
|
|
20
|
+
|
|
21
|
+
#include "Engine/DataTable.h"
|
|
22
|
+
#include "Engine/Texture2D.h"
|
|
23
|
+
#include "HAL/FileManager.h"
|
|
24
|
+
#include "HAL/PlatformProcess.h"
|
|
25
|
+
#include "Misc/AutomationTest.h"
|
|
26
|
+
#include "Misc/Guid.h"
|
|
27
|
+
#include "Misc/PackageName.h"
|
|
28
|
+
#include "Misc/Paths.h"
|
|
29
|
+
#include "UObject/Package.h"
|
|
30
|
+
|
|
31
|
+
namespace
|
|
32
|
+
{
|
|
33
|
+
const TCHAR* const MCPAssetPathTestRoot = TEXT("/UEMCPAssetPathTest/");
|
|
34
|
+
|
|
35
|
+
/** Mount a private content root for the duration of the test and take it
|
|
36
|
+
* back down again, so no assertion here can touch a user's content. */
|
|
37
|
+
struct FScopedAssetPathTestMount
|
|
38
|
+
{
|
|
39
|
+
FString RootPath;
|
|
40
|
+
FString ContentPath;
|
|
41
|
+
|
|
42
|
+
FScopedAssetPathTestMount()
|
|
43
|
+
: RootPath(MCPAssetPathTestRoot)
|
|
44
|
+
, ContentPath(FPaths::Combine(
|
|
45
|
+
FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserTempDir())),
|
|
46
|
+
FString(TEXT("UEMCPAssetPathTest")),
|
|
47
|
+
FGuid::NewGuid().ToString(EGuidFormats::Digits)))
|
|
48
|
+
{
|
|
49
|
+
IFileManager::Get().MakeDirectory(*ContentPath, /*Tree=*/true);
|
|
50
|
+
FPackageName::RegisterMountPoint(RootPath, ContentPath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
~FScopedAssetPathTestMount()
|
|
54
|
+
{
|
|
55
|
+
FPackageName::UnRegisterMountPoint(RootPath, ContentPath);
|
|
56
|
+
IFileManager::Get().DeleteDirectory(*ContentPath, /*RequireExists=*/false, /*Tree=*/true);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
FScopedAssetPathTestMount(const FScopedAssetPathTestMount&) = delete;
|
|
60
|
+
FScopedAssetPathTestMount& operator=(const FScopedAssetPathTestMount&) = delete;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Read one string field off an error response. */
|
|
64
|
+
FString ErrorField(const TSharedPtr<FJsonValue>& Value, const TCHAR* Field)
|
|
65
|
+
{
|
|
66
|
+
if (!Value.IsValid()) return FString();
|
|
67
|
+
const TSharedPtr<FJsonObject> Obj = Value->AsObject();
|
|
68
|
+
if (!Obj.IsValid()) return FString();
|
|
69
|
+
FString Out;
|
|
70
|
+
Obj->TryGetStringField(Field, Out);
|
|
71
|
+
return Out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
bool ErrorSucceeded(const TSharedPtr<FJsonValue>& Value)
|
|
75
|
+
{
|
|
76
|
+
if (!Value.IsValid()) return false;
|
|
77
|
+
const TSharedPtr<FJsonObject> Obj = Value->AsObject();
|
|
78
|
+
if (!Obj.IsValid()) return false;
|
|
79
|
+
bool bSuccess = true;
|
|
80
|
+
Obj->TryGetBoolField(TEXT("success"), bSuccess);
|
|
81
|
+
return bSuccess;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
86
|
+
FMCPAssetPathFormsTest,
|
|
87
|
+
"UE.MCP.Asset.PathResolution.EveryFormDerivesOneObjectPath",
|
|
88
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
89
|
+
|
|
90
|
+
bool FMCPAssetPathFormsTest::RunTest(const FString& Parameters)
|
|
91
|
+
{
|
|
92
|
+
// The bare package path: the form asset(search) reports and #957 rejected.
|
|
93
|
+
{
|
|
94
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(TEXT("/Game/MyFolder/DT_Thing"));
|
|
95
|
+
TestEqual(TEXT("package path from a bare package path"),
|
|
96
|
+
Forms.PackagePath, FString(TEXT("/Game/MyFolder/DT_Thing")));
|
|
97
|
+
TestEqual(TEXT("object path derived from a bare package path"),
|
|
98
|
+
Forms.ObjectPath, FString(TEXT("/Game/MyFolder/DT_Thing.DT_Thing")));
|
|
99
|
+
TestEqual(TEXT("asset name from a bare package path"),
|
|
100
|
+
Forms.AssetName, FString(TEXT("DT_Thing")));
|
|
101
|
+
TestFalse(TEXT("a bare package path carried no object name"), Forms.bInputCarriedObjectName);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The object path: unchanged, and the same object path as the short form.
|
|
105
|
+
{
|
|
106
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(TEXT("/Game/MyFolder/DT_Thing.DT_Thing"));
|
|
107
|
+
TestEqual(TEXT("package path from an object path"),
|
|
108
|
+
Forms.PackagePath, FString(TEXT("/Game/MyFolder/DT_Thing")));
|
|
109
|
+
TestEqual(TEXT("object path from an object path"),
|
|
110
|
+
Forms.ObjectPath, FString(TEXT("/Game/MyFolder/DT_Thing.DT_Thing")));
|
|
111
|
+
TestTrue(TEXT("an object path carried an object name"), Forms.bInputCarriedObjectName);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The export-text form, which arrives from copied editor references.
|
|
115
|
+
{
|
|
116
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(TEXT("DataTable'/Game/MyFolder/DT_Thing.DT_Thing'"));
|
|
117
|
+
TestEqual(TEXT("object path from an export text path"),
|
|
118
|
+
Forms.ObjectPath, FString(TEXT("/Game/MyFolder/DT_Thing.DT_Thing")));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// A subobject path keeps its own leaf, and still names its own package.
|
|
122
|
+
{
|
|
123
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(TEXT("/Game/MyFolder/DT_Thing.DT_Thing:Inner"));
|
|
124
|
+
TestEqual(TEXT("package path from a subobject path"),
|
|
125
|
+
Forms.PackagePath, FString(TEXT("/Game/MyFolder/DT_Thing")));
|
|
126
|
+
TestEqual(TEXT("object path from a subobject path"),
|
|
127
|
+
Forms.ObjectPath, FString(TEXT("/Game/MyFolder/DT_Thing.DT_Thing:Inner")));
|
|
128
|
+
TestEqual(TEXT("asset name from a subobject path"),
|
|
129
|
+
Forms.AssetName, FString(TEXT("Inner")));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Surrounding whitespace is a copy-paste artefact, not a different asset.
|
|
133
|
+
{
|
|
134
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(TEXT(" /Game/MyFolder/DT_Thing "));
|
|
135
|
+
TestEqual(TEXT("whitespace does not change the derived object path"),
|
|
136
|
+
Forms.ObjectPath, FString(TEXT("/Game/MyFolder/DT_Thing.DT_Thing")));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// An empty path derives nothing rather than an object path of ".".
|
|
140
|
+
{
|
|
141
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(FString());
|
|
142
|
+
TestTrue(TEXT("an empty path derives an empty object path"), Forms.ObjectPath.IsEmpty());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
149
|
+
FMCPAssetPathResolutionTest,
|
|
150
|
+
"UE.MCP.Asset.PathResolution.ShortPathResolvesAndMissesAreDiagnosable",
|
|
151
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
152
|
+
|
|
153
|
+
bool FMCPAssetPathResolutionTest::RunTest(const FString& Parameters)
|
|
154
|
+
{
|
|
155
|
+
const FScopedAssetPathTestMount Mount;
|
|
156
|
+
|
|
157
|
+
const FString PackageName = FString(MCPAssetPathTestRoot) + TEXT("DT_PathProbe");
|
|
158
|
+
const FString ObjectPath = PackageName + TEXT(".DT_PathProbe");
|
|
159
|
+
|
|
160
|
+
UPackage* Package = CreatePackage(*PackageName);
|
|
161
|
+
TestNotNull(TEXT("probe package created"), Package);
|
|
162
|
+
if (!Package)
|
|
163
|
+
{
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
UDataTable* Probe = NewObject<UDataTable>(
|
|
168
|
+
Package, FName(TEXT("DT_PathProbe")), RF_Public | RF_Standalone);
|
|
169
|
+
TestNotNull(TEXT("probe asset created"), Probe);
|
|
170
|
+
if (!Probe)
|
|
171
|
+
{
|
|
172
|
+
Package->SetDirtyFlag(false);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
const FGCRootScope KeepProbeAlive(Probe);
|
|
176
|
+
|
|
177
|
+
// #957: this is the assertion that fails on a regression. The short form is
|
|
178
|
+
// what asset(search) reports, and it must resolve to the same object the
|
|
179
|
+
// object path does.
|
|
180
|
+
TestTrue(TEXT("a bare package path resolves to the asset"),
|
|
181
|
+
MCPLoadAssetObject(PackageName) == Probe);
|
|
182
|
+
TestTrue(TEXT("an object path resolves to the same asset"),
|
|
183
|
+
MCPLoadAssetObject(ObjectPath) == Probe);
|
|
184
|
+
TestTrue(TEXT("an export text path resolves to the same asset"),
|
|
185
|
+
MCPLoadAssetObject(FString::Printf(TEXT("DataTable'%s'"), *ObjectPath)) == Probe);
|
|
186
|
+
TestTrue(TEXT("a path with surrounding whitespace resolves to the same asset"),
|
|
187
|
+
MCPLoadAssetObject(FString::Printf(TEXT(" %s "), *PackageName)) == Probe);
|
|
188
|
+
|
|
189
|
+
// The resolver never hands back the package in place of the asset.
|
|
190
|
+
TestTrue(TEXT("the resolver answers the asset, never its package"),
|
|
191
|
+
MCPLoadAssetObject(PackageName) != static_cast<UObject*>(Package));
|
|
192
|
+
|
|
193
|
+
// The typed loader rides on the same resolution, so a short path works for
|
|
194
|
+
// the type-specific readers too (#913).
|
|
195
|
+
TestTrue(TEXT("a typed load resolves a bare package path"),
|
|
196
|
+
LoadAssetByPath<UDataTable>(PackageName) == Probe);
|
|
197
|
+
TestNull(TEXT("a typed load of the wrong type resolves to nothing"),
|
|
198
|
+
LoadAssetByPath<UTexture2D>(PackageName));
|
|
199
|
+
|
|
200
|
+
// A path that resolved to the wrong type says which type it found, so it is
|
|
201
|
+
// distinguishable from a path that resolved to nothing at all.
|
|
202
|
+
{
|
|
203
|
+
const TSharedPtr<FJsonValue> Wrong = MCPAssetLoadError(PackageName, TEXT("Texture2D"));
|
|
204
|
+
TestFalse(TEXT("a wrong-type answer is an error"), ErrorSucceeded(Wrong));
|
|
205
|
+
TestEqual(TEXT("a wrong-type answer names the class that was found"),
|
|
206
|
+
ErrorField(Wrong, TEXT("foundClass")), FString(TEXT("DataTable")));
|
|
207
|
+
TestEqual(TEXT("a wrong-type answer names the class that was expected"),
|
|
208
|
+
ErrorField(Wrong, TEXT("expectedClass")), FString(TEXT("Texture2D")));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// A genuinely missing asset reports the forms that were tried and says the
|
|
212
|
+
// package is not there, rather than a bare "Asset not found".
|
|
213
|
+
{
|
|
214
|
+
const FString MissingPath = FString(MCPAssetPathTestRoot) + TEXT("DT_NotThere");
|
|
215
|
+
TestNull(TEXT("a missing path resolves to nothing"), MCPLoadAssetObject(MissingPath));
|
|
216
|
+
|
|
217
|
+
const TSharedPtr<FJsonValue> Missing = MCPAssetNotFoundError(MissingPath);
|
|
218
|
+
TestFalse(TEXT("a miss is an error"), ErrorSucceeded(Missing));
|
|
219
|
+
TestEqual(TEXT("a miss echoes the caller's own path"),
|
|
220
|
+
ErrorField(Missing, TEXT("assetPath")), MissingPath);
|
|
221
|
+
TestEqual(TEXT("a miss names the normalized package path"),
|
|
222
|
+
ErrorField(Missing, TEXT("packagePath")), MissingPath);
|
|
223
|
+
TestEqual(TEXT("a miss names the object path form it tried"),
|
|
224
|
+
ErrorField(Missing, TEXT("objectPath")), MissingPath + TEXT(".DT_NotThere"));
|
|
225
|
+
TestEqual(TEXT("a miss with nothing on disk and nothing in the registry reads as missing"),
|
|
226
|
+
ErrorField(Missing, TEXT("reason")), FString(TEXT("missing")));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// A context label names what the path was supposed to be.
|
|
230
|
+
{
|
|
231
|
+
const TSharedPtr<FJsonValue> Missing = MCPAssetNotFoundError(
|
|
232
|
+
FString(MCPAssetPathTestRoot) + TEXT("DT_NotThere"), TEXT("Source asset"));
|
|
233
|
+
TestTrue(TEXT("a context label leads the message"),
|
|
234
|
+
ErrorField(Missing, TEXT("error")).StartsWith(TEXT("Source asset not found:")));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Leave nothing dirty behind: a later editor(save_dirty) would otherwise
|
|
238
|
+
// find this package and try to flush it through an unmounted root.
|
|
239
|
+
Package->SetDirtyFlag(false);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#endif
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Regression coverage for #931.
|
|
2
|
+
//
|
|
3
|
+
// asset(set_property) marked the package dirty and stopped there. The value
|
|
4
|
+
// read back correctly, stayed correct for the rest of the editor session, and
|
|
5
|
+
// was gone on the next start, because nothing ever wrote the package. A
|
|
6
|
+
// GameplayAbility whose default reverted that way computed correct values and
|
|
7
|
+
// applied no effect, with nothing pointing at the cause.
|
|
8
|
+
//
|
|
9
|
+
// UPackage::IsDirty is the engine's own record and is the direct check: after a
|
|
10
|
+
// write that persisted, the package is clean; after a write that did not, it is
|
|
11
|
+
// still dirty and the response says why.
|
|
12
|
+
//
|
|
13
|
+
// The test writes nowhere near the attached project. run_automation_tests
|
|
14
|
+
// dispatches every EditorContext/EngineFilter test in the process against
|
|
15
|
+
// whatever project the bridge happens to be attached to, so this registers its
|
|
16
|
+
// own mount point over a fresh directory in the system temp area, does the whole
|
|
17
|
+
// exercise there, then unmounts and deletes it.
|
|
18
|
+
|
|
19
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
20
|
+
|
|
21
|
+
#include "HandlerRegistry.h"
|
|
22
|
+
#include "HandlerUtils.h"
|
|
23
|
+
#include "Handlers/AssetHandlers.h"
|
|
24
|
+
|
|
25
|
+
#include "Dom/JsonObject.h"
|
|
26
|
+
#include "Dom/JsonValue.h"
|
|
27
|
+
#include "Engine/DataTable.h"
|
|
28
|
+
#include "HAL/FileManager.h"
|
|
29
|
+
#include "HAL/PlatformProcess.h"
|
|
30
|
+
#include "Misc/AutomationTest.h"
|
|
31
|
+
#include "Misc/Guid.h"
|
|
32
|
+
#include "Misc/PackageName.h"
|
|
33
|
+
#include "Misc/Paths.h"
|
|
34
|
+
#include "UObject/Package.h"
|
|
35
|
+
#include "UObject/PerPlatformProperties.h"
|
|
36
|
+
|
|
37
|
+
namespace
|
|
38
|
+
{
|
|
39
|
+
const TCHAR* const MCPPersistTestRoot = TEXT("/UEMCPAssetPersistTest/");
|
|
40
|
+
|
|
41
|
+
/** Mount a private content root for the duration of the test and take it
|
|
42
|
+
* back down again, so no assertion here can touch a user's content. */
|
|
43
|
+
struct FScopedPersistTestMount
|
|
44
|
+
{
|
|
45
|
+
FString RootPath;
|
|
46
|
+
FString ContentPath;
|
|
47
|
+
|
|
48
|
+
FScopedPersistTestMount()
|
|
49
|
+
: RootPath(MCPPersistTestRoot)
|
|
50
|
+
, ContentPath(FPaths::Combine(
|
|
51
|
+
FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserTempDir())),
|
|
52
|
+
FString(TEXT("UEMCPAssetPersistTest")),
|
|
53
|
+
FGuid::NewGuid().ToString(EGuidFormats::Digits)))
|
|
54
|
+
{
|
|
55
|
+
IFileManager::Get().MakeDirectory(*ContentPath, /*Tree=*/true);
|
|
56
|
+
FPackageName::RegisterMountPoint(RootPath, ContentPath);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
~FScopedPersistTestMount()
|
|
60
|
+
{
|
|
61
|
+
FPackageName::UnRegisterMountPoint(RootPath, ContentPath);
|
|
62
|
+
IFileManager::Get().DeleteDirectory(*ContentPath, /*RequireExists=*/false, /*Tree=*/true);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
FScopedPersistTestMount(const FScopedPersistTestMount&) = delete;
|
|
66
|
+
FScopedPersistTestMount& operator=(const FScopedPersistTestMount&) = delete;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
TSharedPtr<FJsonObject> ResponseObject(const TSharedPtr<FJsonValue>& Response)
|
|
70
|
+
{
|
|
71
|
+
return (Response.IsValid() && Response->Type == EJson::Object)
|
|
72
|
+
? Response->AsObject()
|
|
73
|
+
: TSharedPtr<FJsonObject>();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
bool ResponseBool(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field, bool bDefault = false)
|
|
77
|
+
{
|
|
78
|
+
const TSharedPtr<FJsonObject> Obj = ResponseObject(Response);
|
|
79
|
+
if (!Obj.IsValid()) return bDefault;
|
|
80
|
+
bool bValue = bDefault;
|
|
81
|
+
Obj->TryGetBoolField(Field, bValue);
|
|
82
|
+
return bValue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
FString ResponseString(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field)
|
|
86
|
+
{
|
|
87
|
+
const TSharedPtr<FJsonObject> Obj = ResponseObject(Response);
|
|
88
|
+
if (!Obj.IsValid()) return FString();
|
|
89
|
+
FString Value;
|
|
90
|
+
Obj->TryGetStringField(Field, Value);
|
|
91
|
+
return Value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
96
|
+
FMCPSetAssetPropertyPersistsTest,
|
|
97
|
+
"UE.MCP.Asset.SetProperty.WriteReachesThePackage",
|
|
98
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
99
|
+
|
|
100
|
+
bool FMCPSetAssetPropertyPersistsTest::RunTest(const FString& Parameters)
|
|
101
|
+
{
|
|
102
|
+
const FScopedPersistTestMount Mount;
|
|
103
|
+
|
|
104
|
+
const FString PackageName = FString(MCPPersistTestRoot) + TEXT("DT_PersistProbe");
|
|
105
|
+
UPackage* Package = CreatePackage(*PackageName);
|
|
106
|
+
if (!TestNotNull(TEXT("probe package created"), Package)) return false;
|
|
107
|
+
|
|
108
|
+
UDataTable* Probe = NewObject<UDataTable>(
|
|
109
|
+
Package, FName(TEXT("DT_PersistProbe")), RF_Public | RF_Standalone);
|
|
110
|
+
if (!TestNotNull(TEXT("probe asset created"), Probe))
|
|
111
|
+
{
|
|
112
|
+
Package->SetDirtyFlag(false);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
Probe->RowStruct = FPerPlatformInt::StaticStruct();
|
|
116
|
+
const FGCRootScope KeepProbeAlive(Probe);
|
|
117
|
+
|
|
118
|
+
FString PackageFileName;
|
|
119
|
+
if (!TestTrue(TEXT("the probe package name maps into the test mount"),
|
|
120
|
+
FPackageName::TryConvertLongPackageNameToFilename(
|
|
121
|
+
PackageName, PackageFileName, FPackageName::GetAssetPackageExtension())))
|
|
122
|
+
{
|
|
123
|
+
Package->SetDirtyFlag(false);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
TestFalse(TEXT("no package file on disk before the write"),
|
|
127
|
+
IFileManager::Get().FileExists(*PackageFileName));
|
|
128
|
+
|
|
129
|
+
FMCPHandlerRegistry Registry;
|
|
130
|
+
FAssetHandlers::RegisterHandlers(Registry);
|
|
131
|
+
if (!TestTrue(TEXT("set_asset_property is registered"), Registry.HasHandler(TEXT("set_asset_property"))))
|
|
132
|
+
{
|
|
133
|
+
Package->SetDirtyFlag(false);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// The default write. This is the #931 assertion: it must reach disk without
|
|
138
|
+
// the caller asking for a save.
|
|
139
|
+
{
|
|
140
|
+
TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
|
|
141
|
+
Params->SetStringField(TEXT("assetPath"), PackageName);
|
|
142
|
+
Params->SetStringField(TEXT("propertyName"), TEXT("ImportKeyField"));
|
|
143
|
+
Params->SetStringField(TEXT("value"), TEXT("PersistedKey"));
|
|
144
|
+
|
|
145
|
+
const TSharedPtr<FJsonValue> Response =
|
|
146
|
+
Registry.ExecuteHandler(TEXT("set_asset_property"), Params);
|
|
147
|
+
|
|
148
|
+
TestTrue(FString::Printf(TEXT("the write succeeded (%s)"), *ResponseString(Response, TEXT("error"))),
|
|
149
|
+
ResponseBool(Response, TEXT("success")));
|
|
150
|
+
TestEqual(TEXT("the value landed on the asset"),
|
|
151
|
+
Probe->ImportKeyField, FString(TEXT("PersistedKey")));
|
|
152
|
+
|
|
153
|
+
// The two halves of the fix: the response says it persisted, and the
|
|
154
|
+
// engine's own dirty record agrees.
|
|
155
|
+
TestTrue(TEXT("the response reports the write as persisted"),
|
|
156
|
+
ResponseBool(Response, TEXT("persisted")));
|
|
157
|
+
TestEqual(TEXT("the response names the package it wrote"),
|
|
158
|
+
ResponseString(Response, TEXT("packageName")), PackageName);
|
|
159
|
+
TestFalse(TEXT("the package is no longer dirty after a persisted write"), Package->IsDirty());
|
|
160
|
+
TestTrue(TEXT("the package file exists on disk after the write"),
|
|
161
|
+
IFileManager::Get().FileExists(*PackageFileName));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The opt-out. A write the caller asked not to save is still reported
|
|
165
|
+
// honestly rather than as a plain success.
|
|
166
|
+
{
|
|
167
|
+
TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
|
|
168
|
+
Params->SetStringField(TEXT("assetPath"), PackageName);
|
|
169
|
+
Params->SetStringField(TEXT("propertyName"), TEXT("ImportKeyField"));
|
|
170
|
+
Params->SetStringField(TEXT("value"), TEXT("InMemoryOnly"));
|
|
171
|
+
Params->SetBoolField(TEXT("save"), false);
|
|
172
|
+
|
|
173
|
+
const TSharedPtr<FJsonValue> Response =
|
|
174
|
+
Registry.ExecuteHandler(TEXT("set_asset_property"), Params);
|
|
175
|
+
|
|
176
|
+
TestTrue(TEXT("an unsaved write still reports the write itself as done"),
|
|
177
|
+
ResponseBool(Response, TEXT("success")));
|
|
178
|
+
TestEqual(TEXT("the value landed on the asset in memory"),
|
|
179
|
+
Probe->ImportKeyField, FString(TEXT("InMemoryOnly")));
|
|
180
|
+
TestFalse(TEXT("an unsaved write reports persisted=false"),
|
|
181
|
+
ResponseBool(Response, TEXT("persisted"), true));
|
|
182
|
+
TestFalse(TEXT("an unsaved write names the reason"),
|
|
183
|
+
ResponseString(Response, TEXT("persistError")).IsEmpty());
|
|
184
|
+
TestTrue(TEXT("an unsaved write leaves the package dirty"), Package->IsDirty());
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Leave nothing dirty behind: a later editor(save_dirty) would otherwise
|
|
188
|
+
// find this package and try to flush it through an unmounted root.
|
|
189
|
+
Package->SetDirtyFlag(false);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
194
|
+
FMCPSetAssetPropertyProtectedMountTest,
|
|
195
|
+
"UE.MCP.Asset.SetProperty.ProtectedMountIsNeverWritten",
|
|
196
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
197
|
+
|
|
198
|
+
bool FMCPSetAssetPropertyProtectedMountTest::RunTest(const FString& Parameters)
|
|
199
|
+
{
|
|
200
|
+
// The persistence step routes through the shared protected-mount guardrail,
|
|
201
|
+
// so a write that lands on engine content is reported as unpersisted rather
|
|
202
|
+
// than written. Asserting the guardrail directly keeps this test off any
|
|
203
|
+
// engine package.
|
|
204
|
+
TestTrue(TEXT("engine content is a protected mount"),
|
|
205
|
+
MCPIsProtectedAssetPath(TEXT("/Engine/EngineMaterials/DefaultMaterial")));
|
|
206
|
+
TestTrue(TEXT("script paths are a protected mount"),
|
|
207
|
+
MCPIsProtectedAssetPath(TEXT("/Script/Engine")));
|
|
208
|
+
TestFalse(TEXT("game content is not a protected mount"),
|
|
209
|
+
MCPIsProtectedAssetPath(TEXT("/Game/MyFolder/DT_Thing")));
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#endif
|