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
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
2
|
+
|
|
3
|
+
#include "Animation/Skeleton.h"
|
|
4
|
+
#include "Engine/SkeletalMesh.h"
|
|
5
|
+
#include "HandlerUtils.h"
|
|
6
|
+
#include "HandlerRegistry.h"
|
|
7
|
+
#include "Handlers/DiffHandlers.h"
|
|
8
|
+
#include "Misc/AutomationTest.h"
|
|
9
|
+
#include "ReferenceSkeleton.h"
|
|
10
|
+
#include "UObject/ObjectSaveContext.h"
|
|
11
|
+
#include "UObject/Package.h"
|
|
12
|
+
|
|
13
|
+
#include <initializer_list>
|
|
14
|
+
|
|
15
|
+
namespace
|
|
16
|
+
{
|
|
17
|
+
struct FTestBone
|
|
18
|
+
{
|
|
19
|
+
const TCHAR* Name;
|
|
20
|
+
int32 ParentIndex;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
class FTransientSkeletonPackage
|
|
24
|
+
{
|
|
25
|
+
public:
|
|
26
|
+
explicit FTransientSkeletonPackage(const TCHAR* TestName)
|
|
27
|
+
{
|
|
28
|
+
const FString PackageName = FString::Printf(
|
|
29
|
+
TEXT("/Temp/UEMCP_%s_%s"),
|
|
30
|
+
TestName,
|
|
31
|
+
*FGuid::NewGuid().ToString(EGuidFormats::Digits));
|
|
32
|
+
Package = CreatePackage(*PackageName);
|
|
33
|
+
// PKG_Transient was removed from EPackageFlags in UE 5.8. RF_Transient on
|
|
34
|
+
// the package object is the supported way to say "never save this".
|
|
35
|
+
Package->SetFlags(RF_Transient);
|
|
36
|
+
Package->SetDirtyFlag(false);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
~FTransientSkeletonPackage()
|
|
40
|
+
{
|
|
41
|
+
if (Package)
|
|
42
|
+
{
|
|
43
|
+
Package->SetDirtyFlag(false);
|
|
44
|
+
Package->MarkAsGarbage();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
USkeleton* AddSkeleton(const TCHAR* Name, std::initializer_list<FTestBone> Bones)
|
|
49
|
+
{
|
|
50
|
+
USkeleton* Skeleton = NewObject<USkeleton>(Package, FName(Name), RF_Transient);
|
|
51
|
+
{
|
|
52
|
+
FReferenceSkeletonModifier Modifier(Skeleton);
|
|
53
|
+
for (const FTestBone& Bone : Bones)
|
|
54
|
+
{
|
|
55
|
+
Modifier.Add(
|
|
56
|
+
FMeshBoneInfo(FName(Bone.Name), Bone.Name, Bone.ParentIndex),
|
|
57
|
+
FTransform::Identity);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return Skeleton;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
USkeletalMesh* AddSkeletalMesh(
|
|
64
|
+
const TCHAR* Name,
|
|
65
|
+
std::initializer_list<FTestBone> Bones,
|
|
66
|
+
USkeleton* AssociatedSkeleton)
|
|
67
|
+
{
|
|
68
|
+
USkeletalMesh* Mesh = NewObject<USkeletalMesh>(Package, FName(Name), RF_Transient);
|
|
69
|
+
FReferenceSkeleton ReferenceSkeleton;
|
|
70
|
+
{
|
|
71
|
+
FReferenceSkeletonModifier Modifier(ReferenceSkeleton, AssociatedSkeleton);
|
|
72
|
+
for (const FTestBone& Bone : Bones)
|
|
73
|
+
{
|
|
74
|
+
Modifier.Add(
|
|
75
|
+
FMeshBoneInfo(FName(Bone.Name), Bone.Name, Bone.ParentIndex),
|
|
76
|
+
FTransform::Identity);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
Mesh->SetRefSkeleton(ReferenceSkeleton);
|
|
80
|
+
Mesh->SetSkeleton(AssociatedSkeleton);
|
|
81
|
+
return Mesh;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
UObject* AddWrongTypeObject(const TCHAR* Name)
|
|
85
|
+
{
|
|
86
|
+
return NewObject<UObject>(Package, FName(Name), RF_Transient);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void ResetDirty() const
|
|
90
|
+
{
|
|
91
|
+
Package->SetDirtyFlag(false);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
UPackage* GetPackage() const
|
|
95
|
+
{
|
|
96
|
+
return Package;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private:
|
|
100
|
+
UPackage* Package = nullptr;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
class FPackageSaveObserver
|
|
104
|
+
{
|
|
105
|
+
public:
|
|
106
|
+
explicit FPackageSaveObserver(UPackage* InPackage)
|
|
107
|
+
: ObservedPackage(InPackage)
|
|
108
|
+
{
|
|
109
|
+
Handle = UPackage::PreSavePackageWithContextEvent.AddLambda(
|
|
110
|
+
[this](UPackage* Package, FObjectPreSaveContext)
|
|
111
|
+
{
|
|
112
|
+
if (Package == ObservedPackage)
|
|
113
|
+
{
|
|
114
|
+
++SaveAttempts;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
~FPackageSaveObserver()
|
|
120
|
+
{
|
|
121
|
+
UPackage::PreSavePackageWithContextEvent.Remove(Handle);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
int32 GetSaveAttempts() const
|
|
125
|
+
{
|
|
126
|
+
return SaveAttempts;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private:
|
|
130
|
+
UPackage* ObservedPackage = nullptr;
|
|
131
|
+
FDelegateHandle Handle;
|
|
132
|
+
int32 SaveAttempts = 0;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
TSharedPtr<FJsonObject> MakeDiffRequest(const FString& FromPath, const FString& ToPath)
|
|
136
|
+
{
|
|
137
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
138
|
+
Request->SetStringField(TEXT("assetPath"), FromPath);
|
|
139
|
+
if (!ToPath.IsEmpty())
|
|
140
|
+
{
|
|
141
|
+
Request->SetStringField(TEXT("otherPath"), ToPath);
|
|
142
|
+
}
|
|
143
|
+
return Request;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
TSharedPtr<FJsonObject> ExecuteDiff(
|
|
147
|
+
FMCPHandlerRegistry& Registry,
|
|
148
|
+
const FString& FromPath,
|
|
149
|
+
const FString& ToPath)
|
|
150
|
+
{
|
|
151
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(
|
|
152
|
+
TEXT("diff_asset"),
|
|
153
|
+
MakeDiffRequest(FromPath, ToPath));
|
|
154
|
+
return Response.IsValid() && Response->Type == EJson::Object
|
|
155
|
+
? Response->AsObject()
|
|
156
|
+
: nullptr;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
TSharedPtr<FJsonObject> ExecuteDiff(
|
|
160
|
+
FMCPHandlerRegistry& Registry,
|
|
161
|
+
const UObject* From,
|
|
162
|
+
const UObject* To)
|
|
163
|
+
{
|
|
164
|
+
return ExecuteDiff(
|
|
165
|
+
Registry,
|
|
166
|
+
From->GetPathName(),
|
|
167
|
+
To ? To->GetPathName() : FString());
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
TArray<FString> GetStringArray(const TSharedPtr<FJsonObject>& Object, const TCHAR* Field)
|
|
171
|
+
{
|
|
172
|
+
TArray<FString> Result;
|
|
173
|
+
const TArray<TSharedPtr<FJsonValue>>* Values = nullptr;
|
|
174
|
+
if (Object.IsValid() && Object->TryGetArrayField(Field, Values) && Values)
|
|
175
|
+
{
|
|
176
|
+
for (const TSharedPtr<FJsonValue>& Value : *Values)
|
|
177
|
+
{
|
|
178
|
+
FString StringValue;
|
|
179
|
+
if (Value.IsValid() && Value->TryGetString(StringValue))
|
|
180
|
+
{
|
|
181
|
+
Result.Add(MoveTemp(StringValue));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return Result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const TArray<TSharedPtr<FJsonValue>>* GetArray(
|
|
189
|
+
const TSharedPtr<FJsonObject>& Object,
|
|
190
|
+
const TCHAR* Field)
|
|
191
|
+
{
|
|
192
|
+
const TArray<TSharedPtr<FJsonValue>>* Values = nullptr;
|
|
193
|
+
return Object.IsValid() && Object->TryGetArrayField(Field, Values) ? Values : nullptr;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
bool AddNamedVirtualBoneForTest(
|
|
197
|
+
USkeleton* Skeleton,
|
|
198
|
+
const FName SourceBone,
|
|
199
|
+
const FName TargetBone,
|
|
200
|
+
const FName DesiredName,
|
|
201
|
+
FName& OutActualName)
|
|
202
|
+
{
|
|
203
|
+
#if UE_MCP_HAS_5_5_API
|
|
204
|
+
OutActualName = DesiredName;
|
|
205
|
+
return Skeleton->AddNewNamedVirtualBone(SourceBone, TargetBone, DesiredName);
|
|
206
|
+
#else
|
|
207
|
+
OutActualName = NAME_None;
|
|
208
|
+
return Skeleton->AddNewVirtualBone(SourceBone, TargetBone, OutActualName);
|
|
209
|
+
#endif
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
214
|
+
FSkeletonDiffRegistrationAndValidationTest,
|
|
215
|
+
"UE.MCP.Diff.Skeleton.RegistrationAndValidation",
|
|
216
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
217
|
+
|
|
218
|
+
bool FSkeletonDiffRegistrationAndValidationTest::RunTest(const FString& Parameters)
|
|
219
|
+
{
|
|
220
|
+
FMCPHandlerRegistry Registry;
|
|
221
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
222
|
+
TestTrue(TEXT("diff_asset is registered"), Registry.HasHandler(TEXT("diff_asset")));
|
|
223
|
+
|
|
224
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffValidation"));
|
|
225
|
+
USkeleton* Skeleton = Fixture.AddSkeleton(TEXT("Skeleton"), {
|
|
226
|
+
{ TEXT("root"), INDEX_NONE },
|
|
227
|
+
});
|
|
228
|
+
UObject* WrongType = Fixture.AddWrongTypeObject(TEXT("WrongType"));
|
|
229
|
+
Fixture.ResetDirty();
|
|
230
|
+
FPackageSaveObserver SaveObserver(Fixture.GetPackage());
|
|
231
|
+
|
|
232
|
+
const TSharedPtr<FJsonObject> MissingOther = ExecuteDiff(Registry, Skeleton, nullptr);
|
|
233
|
+
TestTrue(TEXT("missing otherPath returns an object"), MissingOther.IsValid());
|
|
234
|
+
if (MissingOther.IsValid())
|
|
235
|
+
{
|
|
236
|
+
TestFalse(TEXT("missing otherPath fails"), MissingOther->GetBoolField(TEXT("success")));
|
|
237
|
+
TestTrue(
|
|
238
|
+
TEXT("missing otherPath names the missing parameter"),
|
|
239
|
+
MissingOther->GetStringField(TEXT("error")).Contains(TEXT("otherPath")));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const TSharedPtr<FJsonObject> WrongTypeResult = ExecuteDiff(Registry, Skeleton, WrongType);
|
|
243
|
+
TestTrue(TEXT("wrong-type otherPath returns an object"), WrongTypeResult.IsValid());
|
|
244
|
+
if (WrongTypeResult.IsValid())
|
|
245
|
+
{
|
|
246
|
+
TestFalse(TEXT("wrong-type otherPath fails"), WrongTypeResult->GetBoolField(TEXT("success")));
|
|
247
|
+
const FString Error = WrongTypeResult->GetStringField(TEXT("error"));
|
|
248
|
+
TestTrue(TEXT("wrong type requires an exact Skeleton"), Error.Contains(TEXT("must be exactly a Skeleton")));
|
|
249
|
+
TestTrue(TEXT("wrong type identifies the resolved class"), Error.Contains(TEXT("Object")));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const FString MissingFromPath = Fixture.GetPackage()->GetName() + TEXT(".MissingFrom");
|
|
253
|
+
const TSharedPtr<FJsonObject> MissingFrom = ExecuteDiff(
|
|
254
|
+
Registry,
|
|
255
|
+
MissingFromPath,
|
|
256
|
+
Skeleton->GetPathName());
|
|
257
|
+
TestTrue(TEXT("unloadable assetPath returns an object"), MissingFrom.IsValid());
|
|
258
|
+
if (MissingFrom.IsValid())
|
|
259
|
+
{
|
|
260
|
+
TestFalse(TEXT("unloadable assetPath fails"), MissingFrom->GetBoolField(TEXT("success")));
|
|
261
|
+
const FString Error = MissingFrom->GetStringField(TEXT("error"));
|
|
262
|
+
TestTrue(TEXT("unloadable assetPath has a clear not-found error"), Error.Contains(TEXT("Asset not found")));
|
|
263
|
+
TestTrue(TEXT("unloadable assetPath error identifies the path"), Error.Contains(MissingFromPath));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const FString MissingToPath = Fixture.GetPackage()->GetName() + TEXT(".MissingTo");
|
|
267
|
+
const TSharedPtr<FJsonObject> MissingTo = ExecuteDiff(
|
|
268
|
+
Registry,
|
|
269
|
+
Skeleton->GetPathName(),
|
|
270
|
+
MissingToPath);
|
|
271
|
+
TestTrue(TEXT("unloadable otherPath returns an object"), MissingTo.IsValid());
|
|
272
|
+
if (MissingTo.IsValid())
|
|
273
|
+
{
|
|
274
|
+
TestFalse(TEXT("unloadable otherPath fails"), MissingTo->GetBoolField(TEXT("success")));
|
|
275
|
+
const FString Error = MissingTo->GetStringField(TEXT("error"));
|
|
276
|
+
TestTrue(TEXT("unloadable otherPath has a clear not-found error"), Error.Contains(TEXT("Asset not found")));
|
|
277
|
+
TestTrue(TEXT("unloadable otherPath error identifies the path"), Error.Contains(MissingToPath));
|
|
278
|
+
}
|
|
279
|
+
TestFalse(TEXT("validation does not dirty the fixture package"), Fixture.GetPackage()->IsDirty());
|
|
280
|
+
TestEqual(TEXT("validation never attempts to save the fixture package"), SaveObserver.GetSaveAttempts(), 0);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
285
|
+
FSkeletonDiffIdenticalAndSideEffectsTest,
|
|
286
|
+
"UE.MCP.Diff.Skeleton.IdenticalAndSideEffects",
|
|
287
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
288
|
+
|
|
289
|
+
bool FSkeletonDiffIdenticalAndSideEffectsTest::RunTest(const FString& Parameters)
|
|
290
|
+
{
|
|
291
|
+
FMCPHandlerRegistry Registry;
|
|
292
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
293
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffIdentical"));
|
|
294
|
+
USkeleton* Skeleton = Fixture.AddSkeleton(TEXT("Skeleton"), {
|
|
295
|
+
{ TEXT("root"), INDEX_NONE },
|
|
296
|
+
{ TEXT("pelvis"), 0 },
|
|
297
|
+
});
|
|
298
|
+
Fixture.ResetDirty();
|
|
299
|
+
FPackageSaveObserver SaveObserver(Fixture.GetPackage());
|
|
300
|
+
|
|
301
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, Skeleton, Skeleton);
|
|
302
|
+
TestTrue(TEXT("same-object diff returns an object"), Result.IsValid());
|
|
303
|
+
if (Result.IsValid())
|
|
304
|
+
{
|
|
305
|
+
TestTrue(TEXT("same-object diff succeeds"), Result->GetBoolField(TEXT("success")));
|
|
306
|
+
TestEqual(TEXT("asset type is Skeleton"), Result->GetStringField(TEXT("assetType")), FString(TEXT("Skeleton")));
|
|
307
|
+
TestTrue(TEXT("same-object diff is identical"), Result->GetBoolField(TEXT("identical")));
|
|
308
|
+
TestTrue(TEXT("same-object diff is structurally identical"), Result->GetBoolField(TEXT("structurallyIdentical")));
|
|
309
|
+
TestEqual(TEXT("identical diff has zero changes"), static_cast<int32>(Result->GetNumberField(TEXT("changeCount"))), 0);
|
|
310
|
+
TestEqual(TEXT("raw count is reported"), static_cast<int32>(Result->GetNumberField(TEXT("rawBoneCountFrom"))), 2);
|
|
311
|
+
TestEqual(TEXT("final count is reported"), static_cast<int32>(Result->GetNumberField(TEXT("boneCountFrom"))), 2);
|
|
312
|
+
TestTrue(TEXT("same object is editor-compatible"), Result->GetBoolField(TEXT("editorCompatible")));
|
|
313
|
+
TestTrue(TEXT("identical hierarchy is compatible"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
314
|
+
TestEqual(TEXT("identical diff has no added raw bones"), GetStringArray(Result, TEXT("rawBonesAdded")).Num(), 0);
|
|
315
|
+
}
|
|
316
|
+
TestFalse(TEXT("read-only diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
317
|
+
TestEqual(TEXT("read-only diff never attempts to save its package"), SaveObserver.GetSaveAttempts(), 0);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
322
|
+
FSkeletonDiffFNameIdentityTest,
|
|
323
|
+
"UE.MCP.Diff.Skeleton.FNameIdentity",
|
|
324
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
325
|
+
|
|
326
|
+
bool FSkeletonDiffFNameIdentityTest::RunTest(const FString& Parameters)
|
|
327
|
+
{
|
|
328
|
+
FMCPHandlerRegistry Registry;
|
|
329
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
330
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffFNameIdentity"));
|
|
331
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
332
|
+
{ TEXT("root"), INDEX_NONE },
|
|
333
|
+
{ TEXT("pelvis"), 0 },
|
|
334
|
+
{ TEXT("hand"), 1 },
|
|
335
|
+
});
|
|
336
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
337
|
+
{ TEXT("ROOT"), INDEX_NONE },
|
|
338
|
+
{ TEXT("PELVIS"), 0 },
|
|
339
|
+
{ TEXT("HAND"), 1 },
|
|
340
|
+
});
|
|
341
|
+
FName FromVirtualName;
|
|
342
|
+
FName ToVirtualName;
|
|
343
|
+
TestTrue(
|
|
344
|
+
TEXT("from virtual bone is created"),
|
|
345
|
+
AddNamedVirtualBoneForTest(From, TEXT("pelvis"), TEXT("hand"), TEXT("VB Aim"), FromVirtualName));
|
|
346
|
+
TestTrue(
|
|
347
|
+
TEXT("to virtual bone is created"),
|
|
348
|
+
AddNamedVirtualBoneForTest(To, TEXT("PELVIS"), TEXT("HAND"), TEXT("vb aim"), ToVirtualName));
|
|
349
|
+
TestEqual(TEXT("case-only virtual bone names retain FName identity"), FromVirtualName, ToVirtualName);
|
|
350
|
+
Fixture.ResetDirty();
|
|
351
|
+
|
|
352
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
353
|
+
TestTrue(TEXT("case-only diff succeeds"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
354
|
+
if (Result.IsValid())
|
|
355
|
+
{
|
|
356
|
+
TestTrue(TEXT("FName-equivalent skeletons are structurally identical"), Result->GetBoolField(TEXT("structurallyIdentical")));
|
|
357
|
+
TestTrue(TEXT("FName-equivalent hierarchy is compatible"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
358
|
+
TestEqual(TEXT("case-only names add no changes"), static_cast<int32>(Result->GetNumberField(TEXT("changeCount"))), 0);
|
|
359
|
+
TestEqual(TEXT("case-only raw names are not added"), GetStringArray(Result, TEXT("rawBonesAdded")).Num(), 0);
|
|
360
|
+
TestEqual(TEXT("case-only raw names are not removed"), GetStringArray(Result, TEXT("rawBonesRemoved")).Num(), 0);
|
|
361
|
+
const TArray<TSharedPtr<FJsonValue>>* AddedVirtual = GetArray(Result, TEXT("virtualBonesAdded"));
|
|
362
|
+
const TArray<TSharedPtr<FJsonValue>>* RemovedVirtual = GetArray(Result, TEXT("virtualBonesRemoved"));
|
|
363
|
+
TestTrue(TEXT("case-only virtual names are not added"), AddedVirtual && AddedVirtual->Num() == 0);
|
|
364
|
+
TestTrue(TEXT("case-only virtual names are not removed"), RemovedVirtual && RemovedVirtual->Num() == 0);
|
|
365
|
+
}
|
|
366
|
+
TestFalse(TEXT("case-only diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
371
|
+
FSkeletonDiffDirectionalRawBoneTest,
|
|
372
|
+
"UE.MCP.Diff.Skeleton.DirectionalRawBonesAndOrdering",
|
|
373
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
374
|
+
|
|
375
|
+
bool FSkeletonDiffDirectionalRawBoneTest::RunTest(const FString& Parameters)
|
|
376
|
+
{
|
|
377
|
+
FMCPHandlerRegistry Registry;
|
|
378
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
379
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffRaw"));
|
|
380
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
381
|
+
{ TEXT("root"), INDEX_NONE },
|
|
382
|
+
{ TEXT("RemovedZulu"), 0 },
|
|
383
|
+
{ TEXT("RemovedAlpha"), 0 },
|
|
384
|
+
});
|
|
385
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
386
|
+
{ TEXT("root"), INDEX_NONE },
|
|
387
|
+
{ TEXT("AddedZulu"), 0 },
|
|
388
|
+
{ TEXT("AddedAlpha"), 0 },
|
|
389
|
+
});
|
|
390
|
+
Fixture.ResetDirty();
|
|
391
|
+
|
|
392
|
+
const TSharedPtr<FJsonObject> Forward = ExecuteDiff(Registry, From, To);
|
|
393
|
+
TestTrue(TEXT("forward raw-bone diff succeeds"), Forward.IsValid() && Forward->GetBoolField(TEXT("success")));
|
|
394
|
+
if (Forward.IsValid())
|
|
395
|
+
{
|
|
396
|
+
const TArray<FString> Added = GetStringArray(Forward, TEXT("rawBonesAdded"));
|
|
397
|
+
const TArray<FString> Removed = GetStringArray(Forward, TEXT("rawBonesRemoved"));
|
|
398
|
+
TestEqual(TEXT("two raw bones are added"), Added.Num(), 2);
|
|
399
|
+
TestEqual(TEXT("two raw bones are removed"), Removed.Num(), 2);
|
|
400
|
+
if (Added.Num() == 2)
|
|
401
|
+
{
|
|
402
|
+
TestEqual(TEXT("added bones are sorted first"), Added[0], FString(TEXT("AddedAlpha")));
|
|
403
|
+
TestEqual(TEXT("added bones are sorted second"), Added[1], FString(TEXT("AddedZulu")));
|
|
404
|
+
}
|
|
405
|
+
if (Removed.Num() == 2)
|
|
406
|
+
{
|
|
407
|
+
TestEqual(TEXT("removed bones are sorted first"), Removed[0], FString(TEXT("RemovedAlpha")));
|
|
408
|
+
TestEqual(TEXT("removed bones are sorted second"), Removed[1], FString(TEXT("RemovedZulu")));
|
|
409
|
+
}
|
|
410
|
+
TestTrue(TEXT("additive/removal-only hierarchy stays compatible"), Forward->GetBoolField(TEXT("hierarchyCompatible")));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const TSharedPtr<FJsonObject> Reverse = ExecuteDiff(Registry, To, From);
|
|
414
|
+
TestTrue(TEXT("reverse raw-bone diff succeeds"), Reverse.IsValid() && Reverse->GetBoolField(TEXT("success")));
|
|
415
|
+
if (Reverse.IsValid())
|
|
416
|
+
{
|
|
417
|
+
TestTrue(
|
|
418
|
+
TEXT("reverse added set is the forward removed set"),
|
|
419
|
+
GetStringArray(Reverse, TEXT("rawBonesAdded")) == GetStringArray(Forward, TEXT("rawBonesRemoved")));
|
|
420
|
+
TestTrue(
|
|
421
|
+
TEXT("reverse removed set is the forward added set"),
|
|
422
|
+
GetStringArray(Reverse, TEXT("rawBonesRemoved")) == GetStringArray(Forward, TEXT("rawBonesAdded")));
|
|
423
|
+
}
|
|
424
|
+
TestFalse(TEXT("directional diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
429
|
+
FSkeletonDiffRawBoneIndexTest,
|
|
430
|
+
"UE.MCP.Diff.Skeleton.RawBoneIndexChanges",
|
|
431
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
432
|
+
|
|
433
|
+
bool FSkeletonDiffRawBoneIndexTest::RunTest(const FString& Parameters)
|
|
434
|
+
{
|
|
435
|
+
FMCPHandlerRegistry Registry;
|
|
436
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
437
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffRawIndex"));
|
|
438
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
439
|
+
{ TEXT("root"), INDEX_NONE },
|
|
440
|
+
{ TEXT("SiblingZulu"), 0 },
|
|
441
|
+
{ TEXT("SiblingAlpha"), 0 },
|
|
442
|
+
});
|
|
443
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
444
|
+
{ TEXT("root"), INDEX_NONE },
|
|
445
|
+
{ TEXT("SiblingAlpha"), 0 },
|
|
446
|
+
{ TEXT("SiblingZulu"), 0 },
|
|
447
|
+
});
|
|
448
|
+
Fixture.ResetDirty();
|
|
449
|
+
|
|
450
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
451
|
+
TestTrue(TEXT("raw-index diff succeeds"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
452
|
+
if (Result.IsValid())
|
|
453
|
+
{
|
|
454
|
+
TestFalse(TEXT("sibling reordering is not structurally identical"), Result->GetBoolField(TEXT("structurallyIdentical")));
|
|
455
|
+
TestTrue(TEXT("sibling reordering preserves hierarchy compatibility"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
456
|
+
TestEqual(TEXT("sibling reordering counts two index changes"), static_cast<int32>(Result->GetNumberField(TEXT("changeCount"))), 2);
|
|
457
|
+
TestEqual(TEXT("sibling reordering adds no raw bones"), GetStringArray(Result, TEXT("rawBonesAdded")).Num(), 0);
|
|
458
|
+
TestEqual(TEXT("sibling reordering removes no raw bones"), GetStringArray(Result, TEXT("rawBonesRemoved")).Num(), 0);
|
|
459
|
+
const TArray<TSharedPtr<FJsonValue>>* IndexChanges = GetArray(Result, TEXT("rawBoneIndexChanges"));
|
|
460
|
+
TestTrue(TEXT("raw index changes are present"), IndexChanges && IndexChanges->Num() == 2);
|
|
461
|
+
if (IndexChanges && IndexChanges->Num() == 2)
|
|
462
|
+
{
|
|
463
|
+
const TSharedPtr<FJsonObject> First = (*IndexChanges)[0]->AsObject();
|
|
464
|
+
const TSharedPtr<FJsonObject> Second = (*IndexChanges)[1]->AsObject();
|
|
465
|
+
TestEqual(TEXT("index changes are sorted by bone name"), First->GetStringField(TEXT("bone")), FString(TEXT("SiblingAlpha")));
|
|
466
|
+
TestEqual(TEXT("first old index is exact"), static_cast<int32>(First->GetNumberField(TEXT("fromIndex"))), 2);
|
|
467
|
+
TestEqual(TEXT("first new index is exact"), static_cast<int32>(First->GetNumberField(TEXT("toIndex"))), 1);
|
|
468
|
+
TestEqual(TEXT("second changed bone is exact"), Second->GetStringField(TEXT("bone")), FString(TEXT("SiblingZulu")));
|
|
469
|
+
TestEqual(TEXT("second old index is exact"), static_cast<int32>(Second->GetNumberField(TEXT("fromIndex"))), 1);
|
|
470
|
+
TestEqual(TEXT("second new index is exact"), static_cast<int32>(Second->GetNumberField(TEXT("toIndex"))), 2);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
TestFalse(TEXT("raw-index diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
478
|
+
FSkeletalMeshDiffPublicRouteTest,
|
|
479
|
+
"UE.MCP.Diff.Skeleton.SkeletalMeshPublicRoute",
|
|
480
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
481
|
+
|
|
482
|
+
bool FSkeletalMeshDiffPublicRouteTest::RunTest(const FString& Parameters)
|
|
483
|
+
{
|
|
484
|
+
FMCPHandlerRegistry Registry;
|
|
485
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
486
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletalMeshDiffRoute"));
|
|
487
|
+
USkeleton* FromPolicy = Fixture.AddSkeleton(TEXT("FromPolicy"), {
|
|
488
|
+
{ TEXT("root"), INDEX_NONE },
|
|
489
|
+
{ TEXT("pelvis"), 0 },
|
|
490
|
+
});
|
|
491
|
+
USkeleton* ToPolicy = Fixture.AddSkeleton(TEXT("ToPolicy"), {
|
|
492
|
+
{ TEXT("root"), INDEX_NONE },
|
|
493
|
+
{ TEXT("pelvis"), 0 },
|
|
494
|
+
{ TEXT("hand_attach"), 1 },
|
|
495
|
+
});
|
|
496
|
+
FName FromVirtualName;
|
|
497
|
+
FName ToVirtualName;
|
|
498
|
+
TestTrue(
|
|
499
|
+
TEXT("from policy virtual bone is created"),
|
|
500
|
+
AddNamedVirtualBoneForTest(FromPolicy, TEXT("root"), TEXT("pelvis"), TEXT("VB shared"), FromVirtualName));
|
|
501
|
+
TestTrue(
|
|
502
|
+
TEXT("to policy virtual bone is created"),
|
|
503
|
+
AddNamedVirtualBoneForTest(ToPolicy, TEXT("root"), TEXT("pelvis"), TEXT("VB shared"), ToVirtualName));
|
|
504
|
+
USkeletalMesh* From = Fixture.AddSkeletalMesh(TEXT("SK_From"), {
|
|
505
|
+
{ TEXT("root"), INDEX_NONE },
|
|
506
|
+
{ TEXT("pelvis"), 0 },
|
|
507
|
+
}, FromPolicy);
|
|
508
|
+
USkeletalMesh* To = Fixture.AddSkeletalMesh(TEXT("SK_To"), {
|
|
509
|
+
{ TEXT("root"), INDEX_NONE },
|
|
510
|
+
{ TEXT("pelvis"), 0 },
|
|
511
|
+
{ TEXT("hand_attach"), 1 },
|
|
512
|
+
}, ToPolicy);
|
|
513
|
+
Fixture.ResetDirty();
|
|
514
|
+
FPackageSaveObserver SaveObserver(Fixture.GetPackage());
|
|
515
|
+
|
|
516
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
517
|
+
TestTrue(TEXT("public diff_asset routes SkeletalMesh assets"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
518
|
+
if (Result.IsValid())
|
|
519
|
+
{
|
|
520
|
+
TestEqual(TEXT("asset type is SkeletalMesh"), Result->GetStringField(TEXT("assetType")), FString(TEXT("SkeletalMesh")));
|
|
521
|
+
TestEqual(TEXT("mesh raw count from is compact"), static_cast<int32>(Result->GetNumberField(TEXT("rawBoneCountFrom"))), 2);
|
|
522
|
+
TestEqual(TEXT("mesh raw count to is compact"), static_cast<int32>(Result->GetNumberField(TEXT("rawBoneCountTo"))), 3);
|
|
523
|
+
const TArray<FString> Added = GetStringArray(Result, TEXT("rawBonesAdded"));
|
|
524
|
+
TestEqual(TEXT("mesh diff reports one added bone"), Added.Num(), 1);
|
|
525
|
+
if (Added.Num() == 1)
|
|
526
|
+
{
|
|
527
|
+
TestEqual(TEXT("mesh added bone is exact"), Added[0], FString(TEXT("hand_attach")));
|
|
528
|
+
}
|
|
529
|
+
TestTrue(TEXT("additive mesh hierarchy remains compatible"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
530
|
+
TestEqual(TEXT("mesh policy virtual bones are compared"), static_cast<int32>(Result->GetNumberField(TEXT("virtualBoneCountFrom"))), 1);
|
|
531
|
+
TestEqual(TEXT("from virtual source is the associated Skeleton"), Result->GetStringField(TEXT("virtualBoneSourceFrom")), FromPolicy->GetPathName());
|
|
532
|
+
TestFalse(TEXT("reference-pose omission is explicit"), Result->GetBoolField(TEXT("referencePoseCompared")));
|
|
533
|
+
TestFalse(TEXT("export-name omission is explicit"), Result->GetBoolField(TEXT("exportNamesCompared")));
|
|
534
|
+
}
|
|
535
|
+
TestFalse(TEXT("SkeletalMesh diff stays read-only"), Fixture.GetPackage()->IsDirty());
|
|
536
|
+
TestEqual(TEXT("SkeletalMesh diff never saves"), SaveObserver.GetSaveAttempts(), 0);
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
541
|
+
FSkeletonDiffReparentTest,
|
|
542
|
+
"UE.MCP.Diff.Skeleton.ReparentedHierarchy",
|
|
543
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
544
|
+
|
|
545
|
+
bool FSkeletonDiffReparentTest::RunTest(const FString& Parameters)
|
|
546
|
+
{
|
|
547
|
+
FMCPHandlerRegistry Registry;
|
|
548
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
549
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffReparent"));
|
|
550
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
551
|
+
{ TEXT("root"), INDEX_NONE },
|
|
552
|
+
{ TEXT("ParentA"), 0 },
|
|
553
|
+
{ TEXT("ParentB"), 0 },
|
|
554
|
+
{ TEXT("CommonBone"), 1 },
|
|
555
|
+
});
|
|
556
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
557
|
+
{ TEXT("root"), INDEX_NONE },
|
|
558
|
+
{ TEXT("ParentB"), 0 },
|
|
559
|
+
{ TEXT("ParentA"), 0 },
|
|
560
|
+
{ TEXT("CommonBone"), 1 },
|
|
561
|
+
});
|
|
562
|
+
Fixture.ResetDirty();
|
|
563
|
+
|
|
564
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
565
|
+
TestTrue(TEXT("reparent diff succeeds"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
566
|
+
if (Result.IsValid())
|
|
567
|
+
{
|
|
568
|
+
TestFalse(TEXT("reparented hierarchy is incompatible"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
569
|
+
TestEqual(TEXT("numeric sibling reordering does not look like add/remove"), GetStringArray(Result, TEXT("rawBonesAdded")).Num(), 0);
|
|
570
|
+
const TArray<TSharedPtr<FJsonValue>>* Reparented = GetArray(Result, TEXT("reparented"));
|
|
571
|
+
TestTrue(TEXT("reparented array is present"), Reparented != nullptr);
|
|
572
|
+
if (Reparented && Reparented->Num() == 1)
|
|
573
|
+
{
|
|
574
|
+
const TSharedPtr<FJsonObject> Delta = (*Reparented)[0]->AsObject();
|
|
575
|
+
TestEqual(TEXT("reparented bone is named"), Delta->GetStringField(TEXT("bone")), FString(TEXT("CommonBone")));
|
|
576
|
+
TestEqual(TEXT("old parent is name-derived"), Delta->GetStringField(TEXT("fromParent")), FString(TEXT("ParentA")));
|
|
577
|
+
TestEqual(TEXT("new parent is name-derived"), Delta->GetStringField(TEXT("toParent")), FString(TEXT("ParentB")));
|
|
578
|
+
}
|
|
579
|
+
else
|
|
580
|
+
{
|
|
581
|
+
AddError(TEXT("Expected exactly one reparented bone"));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
TestFalse(TEXT("reparent diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
589
|
+
FSkeletonDiffVirtualBoneTest,
|
|
590
|
+
"UE.MCP.Diff.Skeleton.VirtualBones",
|
|
591
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
592
|
+
|
|
593
|
+
bool FSkeletonDiffVirtualBoneTest::RunTest(const FString& Parameters)
|
|
594
|
+
{
|
|
595
|
+
FMCPHandlerRegistry Registry;
|
|
596
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
597
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffVirtual"));
|
|
598
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
599
|
+
{ TEXT("root"), INDEX_NONE },
|
|
600
|
+
{ TEXT("Source"), 0 },
|
|
601
|
+
{ TEXT("TargetA"), 0 },
|
|
602
|
+
{ TEXT("TargetB"), 0 },
|
|
603
|
+
});
|
|
604
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
605
|
+
{ TEXT("root"), INDEX_NONE },
|
|
606
|
+
{ TEXT("Source"), 0 },
|
|
607
|
+
{ TEXT("TargetA"), 0 },
|
|
608
|
+
{ TEXT("TargetB"), 0 },
|
|
609
|
+
});
|
|
610
|
+
FName FromVirtualName;
|
|
611
|
+
FName ToVirtualName;
|
|
612
|
+
TestTrue(
|
|
613
|
+
TEXT("from virtual bone is created"),
|
|
614
|
+
AddNamedVirtualBoneForTest(From, TEXT("Source"), TEXT("TargetA"), TEXT("VB shared"), FromVirtualName));
|
|
615
|
+
TestTrue(
|
|
616
|
+
TEXT("to virtual bone is created"),
|
|
617
|
+
AddNamedVirtualBoneForTest(To, TEXT("Source"), TEXT("TargetB"), TEXT("VB shared"), ToVirtualName));
|
|
618
|
+
Fixture.ResetDirty();
|
|
619
|
+
|
|
620
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
621
|
+
TestTrue(TEXT("virtual-bone diff succeeds"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
622
|
+
if (Result.IsValid())
|
|
623
|
+
{
|
|
624
|
+
TestTrue(TEXT("virtual endpoint change is non-identical"), !Result->GetBoolField(TEXT("identical")));
|
|
625
|
+
TestTrue(TEXT("virtual-only delta preserves raw hierarchy compatibility"), Result->GetBoolField(TEXT("hierarchyCompatible")));
|
|
626
|
+
TestEqual(TEXT("declared virtual count from is reported"), static_cast<int32>(Result->GetNumberField(TEXT("virtualBoneCountFrom"))), 1);
|
|
627
|
+
TestEqual(TEXT("final reference count includes valid virtual from"), static_cast<int32>(Result->GetNumberField(TEXT("boneCountFrom"))), 5);
|
|
628
|
+
|
|
629
|
+
const TArray<TSharedPtr<FJsonValue>>* Added = GetArray(Result, TEXT("virtualBonesAdded"));
|
|
630
|
+
const TArray<TSharedPtr<FJsonValue>>* Removed = GetArray(Result, TEXT("virtualBonesRemoved"));
|
|
631
|
+
TestTrue(TEXT("changed virtual definition adds one record"), Added && Added->Num() == 1);
|
|
632
|
+
TestTrue(TEXT("changed virtual definition removes one record"), Removed && Removed->Num() == 1);
|
|
633
|
+
if (Added && Added->Num() == 1)
|
|
634
|
+
{
|
|
635
|
+
const TSharedPtr<FJsonObject> Bone = (*Added)[0]->AsObject();
|
|
636
|
+
TestEqual(TEXT("added virtual name"), Bone->GetStringField(TEXT("name")), ToVirtualName.ToString());
|
|
637
|
+
TestEqual(TEXT("added virtual source"), Bone->GetStringField(TEXT("sourceBone")), FString(TEXT("Source")));
|
|
638
|
+
TestEqual(TEXT("added virtual target"), Bone->GetStringField(TEXT("targetBone")), FString(TEXT("TargetB")));
|
|
639
|
+
}
|
|
640
|
+
if (Removed && Removed->Num() == 1)
|
|
641
|
+
{
|
|
642
|
+
const TSharedPtr<FJsonObject> Bone = (*Removed)[0]->AsObject();
|
|
643
|
+
TestEqual(TEXT("removed virtual name"), Bone->GetStringField(TEXT("name")), FromVirtualName.ToString());
|
|
644
|
+
TestEqual(TEXT("removed virtual target"), Bone->GetStringField(TEXT("targetBone")), FString(TEXT("TargetA")));
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
TestFalse(TEXT("virtual diff does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
652
|
+
FSkeletonDiffEditorCompatibilityTest,
|
|
653
|
+
"UE.MCP.Diff.Skeleton.EditorCompatibility",
|
|
654
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
655
|
+
|
|
656
|
+
bool FSkeletonDiffEditorCompatibilityTest::RunTest(const FString& Parameters)
|
|
657
|
+
{
|
|
658
|
+
FMCPHandlerRegistry Registry;
|
|
659
|
+
FDiffHandlers::RegisterHandlers(Registry);
|
|
660
|
+
FTransientSkeletonPackage Fixture(TEXT("SkeletonDiffCompatibility"));
|
|
661
|
+
USkeleton* From = Fixture.AddSkeleton(TEXT("From"), {
|
|
662
|
+
{ TEXT("root"), INDEX_NONE },
|
|
663
|
+
});
|
|
664
|
+
USkeleton* To = Fixture.AddSkeleton(TEXT("To"), {
|
|
665
|
+
{ TEXT("root"), INDEX_NONE },
|
|
666
|
+
});
|
|
667
|
+
To->AddCompatibleSkeleton(From);
|
|
668
|
+
TestFalse(
|
|
669
|
+
TEXT("forward policy query is false before the reverse-compatible diff"),
|
|
670
|
+
From->IsCompatibleForEditor(To));
|
|
671
|
+
Fixture.ResetDirty();
|
|
672
|
+
|
|
673
|
+
const TSharedPtr<FJsonObject> Result = ExecuteDiff(Registry, From, To);
|
|
674
|
+
TestTrue(TEXT("editor-compatibility diff succeeds"), Result.IsValid() && Result->GetBoolField(TEXT("success")));
|
|
675
|
+
if (Result.IsValid())
|
|
676
|
+
{
|
|
677
|
+
TestTrue(TEXT("editorCompatible field is present"), Result->HasTypedField<EJson::Boolean>(TEXT("editorCompatible")));
|
|
678
|
+
TestTrue(TEXT("explicitly registered skeleton is editor-compatible"), Result->GetBoolField(TEXT("editorCompatible")));
|
|
679
|
+
TestTrue(TEXT("hierarchy compatibility remains separately present"), Result->HasTypedField<EJson::Boolean>(TEXT("hierarchyCompatible")));
|
|
680
|
+
}
|
|
681
|
+
TestFalse(TEXT("compatibility query does not dirty its package"), Fixture.GetPackage()->IsDirty());
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
#endif
|