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,170 @@
|
|
|
1
|
+
// Coverage for #916.
|
|
2
|
+
//
|
|
3
|
+
// asset(mesh_boolean) writes a StaticMesh, and everything it can refuse it
|
|
4
|
+
// refuses BEFORE it touches one. These assertions cover that front half: the
|
|
5
|
+
// argument validation, the destructive-form guard, and the protected-mount
|
|
6
|
+
// guardrail, all of which run before the Geometry Script plugin is even asked
|
|
7
|
+
// for. That is deliberate, and it is what makes them assertable on a machine
|
|
8
|
+
// where the plugin is disabled.
|
|
9
|
+
//
|
|
10
|
+
// The boolean itself needs two real meshes and the Geometry Script plugin, so
|
|
11
|
+
// it belongs to the live smoke pass rather than here. Nothing in this file
|
|
12
|
+
// creates, loads or writes an asset.
|
|
13
|
+
|
|
14
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
15
|
+
|
|
16
|
+
#include "HandlerRegistry.h"
|
|
17
|
+
#include "HandlerUtils.h"
|
|
18
|
+
#include "Handlers/AssetHandlers_MeshBoolean.h"
|
|
19
|
+
|
|
20
|
+
#include "Dom/JsonObject.h"
|
|
21
|
+
#include "Dom/JsonValue.h"
|
|
22
|
+
#include "Misc/AutomationTest.h"
|
|
23
|
+
|
|
24
|
+
namespace
|
|
25
|
+
{
|
|
26
|
+
TSharedPtr<FJsonObject> BooleanResponseObject(const TSharedPtr<FJsonValue>& Response)
|
|
27
|
+
{
|
|
28
|
+
return (Response.IsValid() && Response->Type == EJson::Object)
|
|
29
|
+
? Response->AsObject()
|
|
30
|
+
: TSharedPtr<FJsonObject>();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool BooleanResponseBool(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field, bool bDefault = false)
|
|
34
|
+
{
|
|
35
|
+
const TSharedPtr<FJsonObject> Obj = BooleanResponseObject(Response);
|
|
36
|
+
if (!Obj.IsValid()) return bDefault;
|
|
37
|
+
bool bValue = bDefault;
|
|
38
|
+
Obj->TryGetBoolField(Field, bValue);
|
|
39
|
+
return bValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
FString BooleanResponseString(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field)
|
|
43
|
+
{
|
|
44
|
+
const TSharedPtr<FJsonObject> Obj = BooleanResponseObject(Response);
|
|
45
|
+
if (!Obj.IsValid()) return FString();
|
|
46
|
+
FString Value;
|
|
47
|
+
Obj->TryGetStringField(Field, Value);
|
|
48
|
+
return Value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The shape every case here sends, with one field swapped per case. */
|
|
52
|
+
TSharedPtr<FJsonObject> BaseBooleanParams()
|
|
53
|
+
{
|
|
54
|
+
TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
|
|
55
|
+
Params->SetStringField(TEXT("operation"), TEXT("subtract"));
|
|
56
|
+
Params->SetStringField(TEXT("targetPath"), TEXT("/Game/UEMCPNeverExists/SM_Target"));
|
|
57
|
+
Params->SetStringField(TEXT("toolPath"), TEXT("/Game/UEMCPNeverExists/SM_Tool"));
|
|
58
|
+
return Params;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
63
|
+
FMCPMeshBooleanRefusesBeforeWritingTest,
|
|
64
|
+
"UE.MCP.Asset.MeshBoolean.RefusesBeforeItTouchesAnAsset",
|
|
65
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
66
|
+
|
|
67
|
+
bool FMCPMeshBooleanRefusesBeforeWritingTest::RunTest(const FString& Parameters)
|
|
68
|
+
{
|
|
69
|
+
FMCPHandlerRegistry Registry;
|
|
70
|
+
FAssetMeshBooleanHandlers::RegisterHandlers(Registry);
|
|
71
|
+
if (!TestTrue(TEXT("mesh_boolean is registered"), Registry.HasHandler(TEXT("mesh_boolean")))) return false;
|
|
72
|
+
|
|
73
|
+
// The boolean can run for minutes on a repair-sized mesh, so it registers a
|
|
74
|
+
// timeout of its own. The client mirrors that number by hand, and this is
|
|
75
|
+
// the assertion that the registration it mirrors actually happened.
|
|
76
|
+
TestEqual(TEXT("mesh_boolean registers its own game-thread timeout"),
|
|
77
|
+
Registry.GetHandlerTimeout(TEXT("mesh_boolean")), 300.0f);
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
81
|
+
Params->SetStringField(TEXT("operation"), TEXT("smoosh"));
|
|
82
|
+
|
|
83
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
84
|
+
TestFalse(TEXT("an unknown operation is refused"),
|
|
85
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
86
|
+
TestTrue(TEXT("an unknown operation lists the ones that exist"),
|
|
87
|
+
BooleanResponseString(Response, TEXT("error")).Contains(TEXT("subtract")));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
// Two destinations, no way to honour both. Guessing here would pick
|
|
92
|
+
// between writing a new asset and overwriting the caller's target.
|
|
93
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
94
|
+
Params->SetBoolField(TEXT("inPlace"), true);
|
|
95
|
+
Params->SetStringField(TEXT("outputPath"), TEXT("/Game/UEMCPNeverExists/SM_Result"));
|
|
96
|
+
|
|
97
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
98
|
+
TestFalse(TEXT("outputPath together with inPlace is refused"),
|
|
99
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
100
|
+
TestTrue(TEXT("the refusal says both were given"),
|
|
101
|
+
BooleanResponseString(Response, TEXT("error")).Contains(TEXT("inPlace")));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
106
|
+
Params->SetStringField(TEXT("lodType"), TEXT("WhicheverIsFine"));
|
|
107
|
+
|
|
108
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
109
|
+
TestFalse(TEXT("an unknown lodType is refused"),
|
|
110
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
111
|
+
TestTrue(TEXT("the refusal lists the LOD selectors"),
|
|
112
|
+
BooleanResponseString(Response, TEXT("error")).Contains(TEXT("MaxAvailable")));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
{
|
|
116
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
117
|
+
Params->SetStringField(TEXT("nanite"), TEXT("maybe"));
|
|
118
|
+
|
|
119
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
120
|
+
TestFalse(TEXT("an unknown nanite mode is refused"),
|
|
121
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
122
|
+
TestTrue(TEXT("the refusal lists the nanite modes"),
|
|
123
|
+
BooleanResponseString(Response, TEXT("error")).Contains(TEXT("inherit")));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
128
|
+
Params->SetStringField(TEXT("onConflict"), TEXT("clobber"));
|
|
129
|
+
|
|
130
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
131
|
+
TestFalse(TEXT("an unknown onConflict is refused"),
|
|
132
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
{
|
|
136
|
+
// The guardrail runs before the Geometry Script plugin is asked for
|
|
137
|
+
// anything, so engine content is refused whether or not the plugin is
|
|
138
|
+
// installed on this machine.
|
|
139
|
+
TSharedPtr<FJsonObject> Params = BaseBooleanParams();
|
|
140
|
+
Params->SetStringField(TEXT("outputPath"), TEXT("/Engine/UEMCPNeverExists/SM_Result"));
|
|
141
|
+
|
|
142
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
|
|
143
|
+
TestFalse(TEXT("an output path on a protected mount is refused"),
|
|
144
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
145
|
+
TestTrue(TEXT("the refusal names the guardrail"),
|
|
146
|
+
BooleanResponseString(Response, TEXT("error")).Contains(TEXT("protected mount")));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
// Everything valid, nothing on disk. Whether the Geometry Script plugin
|
|
151
|
+
// is here or not, the answer is a named failure rather than a crash,
|
|
152
|
+
// and it is one of exactly two reasons.
|
|
153
|
+
const TSharedPtr<FJsonValue> Response =
|
|
154
|
+
Registry.ExecuteHandler(TEXT("mesh_boolean"), BaseBooleanParams());
|
|
155
|
+
TestFalse(TEXT("a missing target mesh is not a success"),
|
|
156
|
+
BooleanResponseBool(Response, TEXT("success"), true));
|
|
157
|
+
|
|
158
|
+
const FString Reason = BooleanResponseString(Response, TEXT("reason"));
|
|
159
|
+
TestTrue(FString::Printf(
|
|
160
|
+
TEXT("the failure is either the missing asset or the missing plugin, not something unnamed (got '%s')"),
|
|
161
|
+
*Reason),
|
|
162
|
+
Reason == TEXT("missing")
|
|
163
|
+
|| Reason == TEXT("notIndexed")
|
|
164
|
+
|| Reason == TEXT("geometry_scripting_unavailable"));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#endif
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Coverage for asset(get_mesh_geometry) and asset(measure_mesh_geometry) that
|
|
2
|
+
// is safe to run anywhere.
|
|
3
|
+
//
|
|
4
|
+
// run_automation_tests dispatches every EditorContext/EngineFilter test in the
|
|
5
|
+
// process when it is called without a filter, against whatever project the
|
|
6
|
+
// bridge is attached to. Both handlers here are reads, so nothing they do can
|
|
7
|
+
// mutate a project, but a test that named a real mesh would still only pass on
|
|
8
|
+
// the machine that had it. Everything below is registration and argument
|
|
9
|
+
// validation, which holds in any project; the geometry itself is exercised by
|
|
10
|
+
// the smoke suite against the dedicated test project.
|
|
11
|
+
|
|
12
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
13
|
+
|
|
14
|
+
#include "HandlerRegistry.h"
|
|
15
|
+
#include "Handlers/AssetHandlers_Geometry.h"
|
|
16
|
+
#include "Misc/AutomationTest.h"
|
|
17
|
+
|
|
18
|
+
namespace
|
|
19
|
+
{
|
|
20
|
+
TSharedPtr<FJsonObject> MakeGeometryResponseObject(const TSharedPtr<FJsonValue>& Response)
|
|
21
|
+
{
|
|
22
|
+
return (Response.IsValid() && Response->Type == EJson::Object) ? Response->AsObject() : nullptr;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
27
|
+
FAssetMeshGeometryRegistrationTest,
|
|
28
|
+
"UE.MCP.Asset.MeshGeometry.RegistrationAndValidation",
|
|
29
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
30
|
+
|
|
31
|
+
bool FAssetMeshGeometryRegistrationTest::RunTest(const FString& Parameters)
|
|
32
|
+
{
|
|
33
|
+
FMCPHandlerRegistry Registry;
|
|
34
|
+
FAssetGeometryHandlers::RegisterHandlers(Registry);
|
|
35
|
+
TestTrue(TEXT("get_mesh_geometry is registered"), Registry.HasHandler(TEXT("get_mesh_geometry")));
|
|
36
|
+
TestTrue(TEXT("measure_mesh_geometry is registered"), Registry.HasHandler(TEXT("measure_mesh_geometry")));
|
|
37
|
+
|
|
38
|
+
// Missing assetPath names the parameter it wants.
|
|
39
|
+
{
|
|
40
|
+
const TSharedPtr<FJsonObject> Missing = MakeGeometryResponseObject(
|
|
41
|
+
Registry.ExecuteHandler(TEXT("get_mesh_geometry"), MakeShared<FJsonObject>()));
|
|
42
|
+
TestTrue(TEXT("missing assetPath returns an object"), Missing.IsValid());
|
|
43
|
+
if (Missing.IsValid())
|
|
44
|
+
{
|
|
45
|
+
TestFalse(TEXT("missing assetPath is unsuccessful"), Missing->GetBoolField(TEXT("success")));
|
|
46
|
+
TestTrue(TEXT("missing assetPath names assetPath"),
|
|
47
|
+
Missing->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
{
|
|
51
|
+
const TSharedPtr<FJsonObject> Missing = MakeGeometryResponseObject(
|
|
52
|
+
Registry.ExecuteHandler(TEXT("measure_mesh_geometry"), MakeShared<FJsonObject>()));
|
|
53
|
+
TestTrue(TEXT("measure missing assetPath returns an object"), Missing.IsValid());
|
|
54
|
+
if (Missing.IsValid())
|
|
55
|
+
{
|
|
56
|
+
TestFalse(TEXT("measure missing assetPath is unsuccessful"), Missing->GetBoolField(TEXT("success")));
|
|
57
|
+
TestTrue(TEXT("measure missing assetPath names assetPath"),
|
|
58
|
+
Missing->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// An unknown include token is rejected before anything is loaded, and the
|
|
63
|
+
// message carries the full allowed set so a caller does not have to guess.
|
|
64
|
+
{
|
|
65
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
66
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
|
|
67
|
+
Request->SetArrayField(TEXT("include"), { MakeShared<FJsonValueString>(TEXT("tangents")) });
|
|
68
|
+
const TSharedPtr<FJsonObject> Rejected = MakeGeometryResponseObject(
|
|
69
|
+
Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
|
|
70
|
+
TestTrue(TEXT("bad include returns an object"), Rejected.IsValid());
|
|
71
|
+
if (Rejected.IsValid())
|
|
72
|
+
{
|
|
73
|
+
TestFalse(TEXT("bad include is unsuccessful"), Rejected->GetBoolField(TEXT("success")));
|
|
74
|
+
const FString Error = Rejected->GetStringField(TEXT("error"));
|
|
75
|
+
TestTrue(TEXT("bad include quotes the offending token"), Error.Contains(TEXT("tangents")));
|
|
76
|
+
TestTrue(TEXT("bad include lists the allowed set"), Error.Contains(TEXT("positions")));
|
|
77
|
+
TestTrue(TEXT("bad include lists triangles too"), Error.Contains(TEXT("triangles")));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// An empty include is a request for nothing, which is a mistake rather than
|
|
82
|
+
// an answer, so it is refused instead of returning empty section arrays.
|
|
83
|
+
{
|
|
84
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
85
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
|
|
86
|
+
Request->SetArrayField(TEXT("include"), TArray<TSharedPtr<FJsonValue>>());
|
|
87
|
+
const TSharedPtr<FJsonObject> Rejected = MakeGeometryResponseObject(
|
|
88
|
+
Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
|
|
89
|
+
TestTrue(TEXT("empty include returns an object"), Rejected.IsValid());
|
|
90
|
+
if (Rejected.IsValid())
|
|
91
|
+
{
|
|
92
|
+
TestFalse(TEXT("empty include is unsuccessful"), Rejected->GetBoolField(TEXT("success")));
|
|
93
|
+
TestTrue(TEXT("empty include says what to pass"),
|
|
94
|
+
Rejected->GetStringField(TEXT("error")).Contains(TEXT("include")));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// A path that resolves to nothing reports the path, not a null dereference.
|
|
99
|
+
{
|
|
100
|
+
TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
|
|
101
|
+
Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
|
|
102
|
+
const TSharedPtr<FJsonObject> NotFound = MakeGeometryResponseObject(
|
|
103
|
+
Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
|
|
104
|
+
TestTrue(TEXT("unknown asset returns an object"), NotFound.IsValid());
|
|
105
|
+
if (NotFound.IsValid())
|
|
106
|
+
{
|
|
107
|
+
TestFalse(TEXT("unknown asset is unsuccessful"), NotFound->GetBoolField(TEXT("success")));
|
|
108
|
+
TestTrue(TEXT("unknown asset echoes the path"),
|
|
109
|
+
NotFound->GetStringField(TEXT("error")).Contains(TEXT("DoesNotExist_MeshGeometryTest")));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#endif // WITH_DEV_AUTOMATION_TESTS
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Regression coverage for #949.
|
|
2
|
+
//
|
|
3
|
+
// SaveAssetPackage built its target filename with the asset extension
|
|
4
|
+
// unconditionally, so every handler that mutated an actor or a component in the
|
|
5
|
+
// open level wrote Content/Maps/<Level>.uasset alongside the existing
|
|
6
|
+
// Content/Maps/<Level>.umap. Two package files then claimed the same long
|
|
7
|
+
// package name, level(save) kept writing one and the ~87 SaveAssetPackage call
|
|
8
|
+
// sites kept writing the other, and the two diverged in silence. Unreal resolves
|
|
9
|
+
// the ".uasset" first, so the fork is the copy that wins.
|
|
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/World.h"
|
|
22
|
+
#include "HAL/FileManager.h"
|
|
23
|
+
#include "HAL/PlatformProcess.h"
|
|
24
|
+
#include "Misc/AutomationTest.h"
|
|
25
|
+
#include "Misc/Guid.h"
|
|
26
|
+
#include "Misc/PackageName.h"
|
|
27
|
+
#include "Misc/Paths.h"
|
|
28
|
+
#include "UObject/Package.h"
|
|
29
|
+
|
|
30
|
+
namespace
|
|
31
|
+
{
|
|
32
|
+
const TCHAR* const MCPExtensionTestRoot = TEXT("/UEMCPPackageExtensionTest/");
|
|
33
|
+
|
|
34
|
+
/** Mount a private content root for the duration of the test and take it
|
|
35
|
+
* back down again, so no assertion here can touch a user's content. */
|
|
36
|
+
struct FScopedTestMount
|
|
37
|
+
{
|
|
38
|
+
FString RootPath;
|
|
39
|
+
FString ContentPath;
|
|
40
|
+
|
|
41
|
+
FScopedTestMount()
|
|
42
|
+
: RootPath(MCPExtensionTestRoot)
|
|
43
|
+
, ContentPath(FPaths::Combine(
|
|
44
|
+
FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserTempDir())),
|
|
45
|
+
FString(TEXT("UEMCPPackageExtensionTest")),
|
|
46
|
+
FGuid::NewGuid().ToString(EGuidFormats::Digits)))
|
|
47
|
+
{
|
|
48
|
+
IFileManager::Get().MakeDirectory(*ContentPath, /*Tree=*/true);
|
|
49
|
+
FPackageName::RegisterMountPoint(RootPath, ContentPath);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
~FScopedTestMount()
|
|
53
|
+
{
|
|
54
|
+
FPackageName::UnRegisterMountPoint(RootPath, ContentPath);
|
|
55
|
+
IFileManager::Get().DeleteDirectory(*ContentPath, /*RequireExists=*/false, /*Tree=*/true);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
FScopedTestMount(const FScopedTestMount&) = delete;
|
|
59
|
+
FScopedTestMount& operator=(const FScopedTestMount&) = delete;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
64
|
+
FPackageSaveExtensionTest,
|
|
65
|
+
"UE.MCP.Save.PackageExtension.WorldPackagesWriteUMap",
|
|
66
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
67
|
+
|
|
68
|
+
bool FPackageSaveExtensionTest::RunTest(const FString& Parameters)
|
|
69
|
+
{
|
|
70
|
+
const FScopedTestMount Mount;
|
|
71
|
+
IFileManager& FM = IFileManager::Get();
|
|
72
|
+
|
|
73
|
+
// A package with no world in it keeps the asset extension. This is the half
|
|
74
|
+
// the old code got right, and it has to stay right: one-file-per-actor
|
|
75
|
+
// packages under __ExternalActors__ hold an AActor and no UWorld, and OFPA
|
|
76
|
+
// expects those on disk as ".uasset".
|
|
77
|
+
const FString AssetPackageName = FString(MCPExtensionTestRoot) + TEXT("AssetPackage");
|
|
78
|
+
UPackage* AssetPackage = CreatePackage(*AssetPackageName);
|
|
79
|
+
TestNotNull(TEXT("asset package created"), AssetPackage);
|
|
80
|
+
if (AssetPackage)
|
|
81
|
+
{
|
|
82
|
+
TestFalse(TEXT("a package with no world is not a map package"), IsMapPackage(AssetPackage));
|
|
83
|
+
TestEqual(TEXT("a package with no world takes the asset extension"),
|
|
84
|
+
PackageFileExtension(AssetPackage), FPackageName::GetAssetPackageExtension());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The regression itself: an object whose outermost is a UWorld.
|
|
88
|
+
const FString WorldPackageName = FString(MCPExtensionTestRoot) + TEXT("TestLevel");
|
|
89
|
+
UPackage* WorldPackage = CreatePackage(*WorldPackageName);
|
|
90
|
+
TestNotNull(TEXT("world package created"), WorldPackage);
|
|
91
|
+
if (!WorldPackage)
|
|
92
|
+
{
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
UWorld* World = UWorld::CreateWorld(
|
|
97
|
+
EWorldType::Inactive,
|
|
98
|
+
/*bInformEngineOfWorld=*/false,
|
|
99
|
+
FName(TEXT("TestLevel")),
|
|
100
|
+
WorldPackage,
|
|
101
|
+
/*bAddToRoot=*/false);
|
|
102
|
+
TestNotNull(TEXT("world created inside the package"), World);
|
|
103
|
+
if (!World)
|
|
104
|
+
{
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
// Mirror what UWorldFactory stamps on a newly authored map, so the save has
|
|
108
|
+
// a top-level asset to pick up under SaveAssetPackage's TopLevelFlags.
|
|
109
|
+
World->SetFlags(RF_Public | RF_Standalone);
|
|
110
|
+
const FGCRootScope KeepWorldAlive(World);
|
|
111
|
+
|
|
112
|
+
TestTrue(TEXT("the world's outermost is the world package"), World->GetOutermost() == WorldPackage);
|
|
113
|
+
TestTrue(TEXT("a package holding a world is a map package"), IsMapPackage(WorldPackage));
|
|
114
|
+
TestEqual(TEXT("a world package takes the map extension"),
|
|
115
|
+
PackageFileExtension(WorldPackage), FPackageName::GetMapPackageExtension());
|
|
116
|
+
|
|
117
|
+
FString ResolvedFileName;
|
|
118
|
+
TestTrue(TEXT("the world package resolves to a filename"),
|
|
119
|
+
ResolvePackageFileName(WorldPackage, ResolvedFileName));
|
|
120
|
+
TestTrue(TEXT("the resolved filename is a .umap"),
|
|
121
|
+
ResolvedFileName.EndsWith(FPackageName::GetMapPackageExtension(), ESearchCase::IgnoreCase));
|
|
122
|
+
TestFalse(TEXT("the resolved filename is not a .uasset"),
|
|
123
|
+
ResolvedFileName.EndsWith(FPackageName::GetAssetPackageExtension(), ESearchCase::IgnoreCase));
|
|
124
|
+
|
|
125
|
+
FString BaseFileName;
|
|
126
|
+
TestTrue(TEXT("the package name maps into the test mount"),
|
|
127
|
+
FPackageName::TryConvertLongPackageNameToFilename(WorldPackageName, BaseFileName));
|
|
128
|
+
const FString MapFile = BaseFileName + FPackageName::GetMapPackageExtension();
|
|
129
|
+
const FString AssetSibling = BaseFileName + FPackageName::GetAssetPackageExtension();
|
|
130
|
+
|
|
131
|
+
TestFalse(TEXT("no map file on disk before the save"), FM.FileExists(*MapFile));
|
|
132
|
+
TestFalse(TEXT("no asset sibling on disk before the save"), FM.FileExists(*AssetSibling));
|
|
133
|
+
|
|
134
|
+
const bool bSaved = SaveAssetPackage(World);
|
|
135
|
+
|
|
136
|
+
// This is the assertion that fails on a #949 regression, and it holds
|
|
137
|
+
// whatever the save itself decided to do: the asset sibling must never be
|
|
138
|
+
// created for a package that already lives on disk as a map.
|
|
139
|
+
TestFalse(TEXT("saving a world package writes no .uasset sibling"), FM.FileExists(*AssetSibling));
|
|
140
|
+
if (bSaved)
|
|
141
|
+
{
|
|
142
|
+
TestTrue(TEXT("saving a world package writes the .umap"), FM.FileExists(*MapFile));
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
{
|
|
146
|
+
AddInfo(TEXT("SavePackage declined to write the test world; the .uasset sibling assertion still stands."));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Leave nothing dirty behind: a later editor(save_dirty) would otherwise
|
|
150
|
+
// find these packages and try to flush them through an unmounted root.
|
|
151
|
+
WorldPackage->SetDirtyFlag(false);
|
|
152
|
+
if (AssetPackage)
|
|
153
|
+
{
|
|
154
|
+
AssetPackage->SetDirtyFlag(false);
|
|
155
|
+
}
|
|
156
|
+
World->DestroyWorld(/*bInformEngineOfWorld=*/false);
|
|
157
|
+
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#endif
|