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
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include "Engine/Level.h"
|
|
15
15
|
#include "Engine/LevelScriptBlueprint.h"
|
|
16
16
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
17
|
+
#include "Kismet2/BlueprintEditorUtils.h"
|
|
17
18
|
#include "Subsystems/AssetEditorSubsystem.h"
|
|
18
19
|
#include "Editor.h"
|
|
19
20
|
#include "FileHelpers.h"
|
|
@@ -75,17 +76,11 @@
|
|
|
75
76
|
#include "AI/Navigation/NavCollisionBase.h"
|
|
76
77
|
|
|
77
78
|
// ─── Protected mount guardrail ──────────────────────────────────────────
|
|
78
|
-
// The rule itself is MCPIsProtectedAssetPath in HandlerUtils.h,
|
|
79
|
-
//
|
|
79
|
+
// The rule itself is MCPIsProtectedAssetPath in HandlerUtils.h, and the
|
|
80
|
+
// refusal it produces is MCPProtectedPathError beside it, shared by every
|
|
81
|
+
// asset translation unit so the write paths cannot drift apart.
|
|
80
82
|
namespace
|
|
81
83
|
{
|
|
82
|
-
TSharedPtr<FJsonValue> MakeProtectedPathError(const FString& Path)
|
|
83
|
-
{
|
|
84
|
-
return MCPError(FString::Printf(
|
|
85
|
-
TEXT("Refusing to mutate protected mount: %s. Engine, /Script/, /Memory/, /Temp/ are read-only via the bridge."),
|
|
86
|
-
*Path));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
84
|
// Split "/Game/Foo/Bar.Bar" (or "/Game/Foo/Bar") into mount "/Game/" + rel "Foo/Bar".
|
|
90
85
|
// Returns false if the path is malformed or has no mount segment.
|
|
91
86
|
bool SplitMountAndRel(const FString& AssetOrPackagePath, FString& OutMountRoot, FString& OutRelPath, FString& OutPackageName, FString& OutAssetName)
|
|
@@ -151,6 +146,9 @@ void FAssetHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
151
146
|
Registry.RegisterHandler(TEXT("delete_asset"), &DeleteAsset);
|
|
152
147
|
Registry.RegisterHandler(TEXT("delete_asset_batch"), &DeleteAssetBatch);
|
|
153
148
|
Registry.RegisterHandler(TEXT("bulk_rename_assets"), &BulkRename);
|
|
149
|
+
// #908: bounded redirector clean-up after a move. Timeout raised because a
|
|
150
|
+
// fix-up loads and resaves every referencing package.
|
|
151
|
+
Registry.RegisterHandlerWithTimeout(TEXT("fixup_redirectors"), &FixupRedirectors, 300.0f);
|
|
154
152
|
Registry.RegisterHandler(TEXT("create_data_asset"), &CreateDataAsset);
|
|
155
153
|
// Bounded batch upsert plus its rollback inverse. A 500-item batch that
|
|
156
154
|
// preflights every item on a transient copy before touching a package can
|
|
@@ -161,6 +159,9 @@ void FAssetHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
161
159
|
// #726: generic create-any-concrete-UObject-class asset (physical materials,
|
|
162
160
|
// curves, settings objects) - not restricted to UDataAsset subclasses.
|
|
163
161
|
Registry.RegisterHandler(TEXT("create_asset_by_class"), &CreateAssetByClass);
|
|
162
|
+
// #975: a named subobject inside an existing asset package, which
|
|
163
|
+
// create_asset_by_class cannot make because it always creates a package.
|
|
164
|
+
Registry.RegisterHandler(TEXT("create_subobject"), &CreateSubobject);
|
|
164
165
|
Registry.RegisterHandler(TEXT("save_asset"), &SaveAsset);
|
|
165
166
|
Registry.RegisterHandler(TEXT("save_all_dirty"), &SaveAllDirty);
|
|
166
167
|
Registry.RegisterHandler(TEXT("list_textures"), &ListTextures);
|
|
@@ -242,6 +243,7 @@ void FAssetHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
242
243
|
Registry.RegisterHandler(TEXT("set_stringtable_entry"), &SetStringTableEntry);
|
|
243
244
|
Registry.RegisterHandler(TEXT("remove_stringtable_entry"), &RemoveStringTableEntry);
|
|
244
245
|
Registry.RegisterHandler(TEXT("import_stringtable"), &ImportStringTable);
|
|
246
|
+
Registry.RegisterHandler(TEXT("import_stringtable_csv"), &ImportStringTableCsv);
|
|
245
247
|
|
|
246
248
|
// v0.7.8 stubs - FTS5-backed asset search
|
|
247
249
|
Registry.RegisterHandler(TEXT("search_assets_fts"), &SearchAssetsFTS);
|
|
@@ -563,12 +565,10 @@ TSharedPtr<FJsonValue> FAssetHandlers::ReadAsset(const TSharedPtr<FJsonObject>&
|
|
|
563
565
|
FString AssetPath;
|
|
564
566
|
if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
|
|
565
567
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
Asset = LoadObject<UObject>(nullptr, *AssetPath);
|
|
571
|
-
}
|
|
568
|
+
// The LoadAsset-then-LoadObject pair this action has always used now lives
|
|
569
|
+
// in MCPLoadAssetObject, so the type-specific readers resolve a path the
|
|
570
|
+
// same way this one does (#930).
|
|
571
|
+
UObject* Asset = MCPLoadAssetObject(AssetPath);
|
|
572
572
|
if (!Asset)
|
|
573
573
|
{
|
|
574
574
|
return MCPError(FString::Printf(TEXT("Asset not found: %s"), *AssetPath));
|
|
@@ -693,14 +693,21 @@ TSharedPtr<FJsonValue> FAssetHandlers::ReadAsset(const TSharedPtr<FJsonObject>&
|
|
|
693
693
|
// own properties (ParentClass, etc.) are rarely what a caller wants. Resolve to
|
|
694
694
|
// the generated-class CDO so asset property reads/writes hit the real defaults
|
|
695
695
|
// and can author Instanced sub-object arrays. Non-Blueprint assets pass through.
|
|
696
|
-
|
|
696
|
+
//
|
|
697
|
+
// OutBlueprint carries the wrapper back out, because a write to the CDO has
|
|
698
|
+
// bookkeeping the CDO alone cannot do: the package to save is the Blueprint's,
|
|
699
|
+
// and the compiler's own record of the variable's default has to be reconciled
|
|
700
|
+
// with the value just written (#931).
|
|
701
|
+
static UObject* MCPResolveAssetToCDO(UObject* Asset, UBlueprint** OutBlueprint = nullptr)
|
|
697
702
|
{
|
|
703
|
+
if (OutBlueprint) *OutBlueprint = nullptr;
|
|
698
704
|
if (UBlueprint* BP = Cast<UBlueprint>(Asset))
|
|
699
705
|
{
|
|
700
706
|
if (UClass* GenClass = BP->GeneratedClass)
|
|
701
707
|
{
|
|
702
708
|
if (UObject* CDO = GenClass->GetDefaultObject())
|
|
703
709
|
{
|
|
710
|
+
if (OutBlueprint) *OutBlueprint = BP;
|
|
704
711
|
return CDO;
|
|
705
712
|
}
|
|
706
713
|
}
|
|
@@ -708,16 +715,124 @@ static UObject* MCPResolveAssetToCDO(UObject* Asset)
|
|
|
708
715
|
return Asset;
|
|
709
716
|
}
|
|
710
717
|
|
|
718
|
+
// #931: a property write marked the package dirty and stopped there, so it read
|
|
719
|
+
// back correctly, survived until the editor closed, and was gone on the next
|
|
720
|
+
// start. A GameplayAbility whose default reverted computed correct values and
|
|
721
|
+
// applied no effect, with nothing pointing at the cause. A write now either
|
|
722
|
+
// reaches the package on disk or says why it did not, and never reports plain
|
|
723
|
+
// success for a change that only exists in memory.
|
|
724
|
+
//
|
|
725
|
+
// Returns true when the package was written. On false, OutReason carries the
|
|
726
|
+
// sentence to hand back to the caller.
|
|
727
|
+
static bool MCPPersistAssetWrite(UObject* Asset, UBlueprint* OwningBlueprint, bool bSave, FString& OutReason)
|
|
728
|
+
{
|
|
729
|
+
OutReason.Reset();
|
|
730
|
+
UPackage* Package = Asset ? Asset->GetOutermost() : nullptr;
|
|
731
|
+
if (!Package)
|
|
732
|
+
{
|
|
733
|
+
OutReason = TEXT("The asset has no package, so there is nothing to write.");
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const FString PackageName = Package->GetName();
|
|
738
|
+
if (!bSave)
|
|
739
|
+
{
|
|
740
|
+
OutReason = FString::Printf(
|
|
741
|
+
TEXT("save=false was requested, so '%s' is dirty in memory only and the change is lost when the editor closes. ")
|
|
742
|
+
TEXT("Call asset(save) for it, or repeat the write with save=true."),
|
|
743
|
+
*PackageName);
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
// Protected mounts and read-only package files are both answered before the
|
|
747
|
+
// engine is asked to write, by the one shared guard (#932): asking it to
|
|
748
|
+
// open a file it cannot is what took the editor down with a fatal error.
|
|
749
|
+
// For a Blueprint the package's asset is the UBlueprint; the CDO is one of
|
|
750
|
+
// its exports and rides along, so the guard and the save see the same
|
|
751
|
+
// object.
|
|
752
|
+
UObject* WriteTarget = OwningBlueprint ? static_cast<UObject*>(OwningBlueprint) : Asset;
|
|
753
|
+
return SaveAssetPackageChecked(WriteTarget, OutReason);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// A CDO write is a genuine package export and survives both the save and a
|
|
757
|
+
// later recompile, but two pieces of editor bookkeeping do not happen on their
|
|
758
|
+
// own, and both read as "my write did not take" (#931).
|
|
759
|
+
//
|
|
760
|
+
// UBlueprintGeneratedClass keeps a post-construct property list that newly
|
|
761
|
+
// spawned instances initialise from, and only MarkBlueprintAsModified rebuilds
|
|
762
|
+
// it. And FBPVariableDescription::DefaultValue, when it is not empty, is
|
|
763
|
+
// replayed onto the CDO by the next full compile AFTER the old CDO's values are
|
|
764
|
+
// copied forward, so a stale string default silently overwrites the value that
|
|
765
|
+
// was just written. Clearing it makes the CDO the single record of the default,
|
|
766
|
+
// which is what the Blueprint editor's own Class Defaults panel does.
|
|
767
|
+
static void MCPNoteBlueprintCDOWrite(UBlueprint* Blueprint, const FString& PropertyPath)
|
|
768
|
+
{
|
|
769
|
+
if (!Blueprint) return;
|
|
770
|
+
|
|
771
|
+
// The root segment is the variable the compiler knows by name. A dotted or
|
|
772
|
+
// indexed path lands inside that variable's value, and the string default
|
|
773
|
+
// it would replay covers the whole variable either way.
|
|
774
|
+
FString RootName = PropertyPath;
|
|
775
|
+
int32 Cut = INDEX_NONE;
|
|
776
|
+
if (RootName.FindChar(TEXT('.'), Cut)) RootName = RootName.Left(Cut);
|
|
777
|
+
if (RootName.FindChar(TEXT('['), Cut)) RootName = RootName.Left(Cut);
|
|
778
|
+
const FName RootVarName(*RootName);
|
|
779
|
+
|
|
780
|
+
FProperty* ChangedProperty = nullptr;
|
|
781
|
+
if (UClass* GenClass = Blueprint->GeneratedClass)
|
|
782
|
+
{
|
|
783
|
+
ChangedProperty = GenClass->FindPropertyByName(RootVarName);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
for (FBPVariableDescription& Variable : Blueprint->NewVariables)
|
|
787
|
+
{
|
|
788
|
+
if (Variable.VarName == RootVarName)
|
|
789
|
+
{
|
|
790
|
+
Variable.DefaultValue.Empty();
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
FPropertyChangedEvent ChangeEvent(ChangedProperty, EPropertyChangeType::ValueSet);
|
|
796
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint, ChangeEvent);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** Report where a property write ended up: the package, whether it reached
|
|
800
|
+
* disk, and the reason when it did not. A write the caller asked to persist
|
|
801
|
+
* and that did not persist is a failure, not a success with a footnote. */
|
|
802
|
+
static void MCPDescribePropertyWritePersistence(
|
|
803
|
+
TSharedPtr<FJsonObject>& Result,
|
|
804
|
+
UObject* Asset,
|
|
805
|
+
const FString& PropertyName,
|
|
806
|
+
bool bSaveRequested,
|
|
807
|
+
bool bPersisted,
|
|
808
|
+
const FString& PersistReason)
|
|
809
|
+
{
|
|
810
|
+
if (UPackage* Package = Asset ? Asset->GetOutermost() : nullptr)
|
|
811
|
+
{
|
|
812
|
+
Result->SetStringField(TEXT("packageName"), Package->GetName());
|
|
813
|
+
Result->SetBoolField(TEXT("packageDirty"), Package->IsDirty());
|
|
814
|
+
}
|
|
815
|
+
Result->SetBoolField(TEXT("persisted"), bPersisted);
|
|
816
|
+
Result->SetBoolField(TEXT("saved"), bPersisted);
|
|
817
|
+
if (bPersisted) return;
|
|
818
|
+
|
|
819
|
+
Result->SetStringField(TEXT("persistError"), PersistReason);
|
|
820
|
+
if (bSaveRequested)
|
|
821
|
+
{
|
|
822
|
+
Result->SetBoolField(TEXT("success"), false);
|
|
823
|
+
Result->SetStringField(TEXT("error"), FString::Printf(
|
|
824
|
+
TEXT("Set '%s' in memory but could not persist it: %s"), *PropertyName, *PersistReason));
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
711
828
|
TSharedPtr<FJsonValue> FAssetHandlers::ReadAssetProperties(const TSharedPtr<FJsonObject>& Params)
|
|
712
829
|
{
|
|
713
830
|
FString AssetPath;
|
|
714
831
|
if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
|
|
715
832
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return MCPError(FString::Printf(TEXT("Asset not found: %s"), *AssetPath));
|
|
720
|
-
}
|
|
833
|
+
TSharedPtr<FJsonValue> LoadError;
|
|
834
|
+
UObject* Asset = MCPRequireAssetObject(AssetPath, LoadError);
|
|
835
|
+
if (!Asset) return LoadError;
|
|
721
836
|
Asset = MCPResolveAssetToCDO(Asset); // #568
|
|
722
837
|
|
|
723
838
|
FString ValueFormat;
|
|
@@ -978,17 +1093,11 @@ TSharedPtr<FJsonValue> FAssetHandlers::DuplicateAsset(const TSharedPtr<FJsonObje
|
|
|
978
1093
|
const FString OnConflict = OptionalString(Params, TEXT("onConflict"), TEXT("skip"));
|
|
979
1094
|
|
|
980
1095
|
// #441: DoesAssetExist returns false for some Blueprints in 5.7 even when
|
|
981
|
-
// the registry/loader can resolve them. Confirm via
|
|
1096
|
+
// the registry/loader can resolve them. Confirm via the shared resolver
|
|
982
1097
|
// before erroring out so duplicate doesn't bounce off valid paths.
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
SourceObj = LoadObject<UObject>(nullptr, *SourcePath);
|
|
987
|
-
}
|
|
988
|
-
if (!SourceObj)
|
|
989
|
-
{
|
|
990
|
-
return MCPError(FString::Printf(TEXT("Source asset not found: %s"), *SourcePath));
|
|
991
|
-
}
|
|
1098
|
+
TSharedPtr<FJsonValue> SourceLoadError;
|
|
1099
|
+
UObject* SourceObj = MCPRequireAssetObject(SourcePath, SourceLoadError, TEXT("Source asset"));
|
|
1100
|
+
if (!SourceObj) return SourceLoadError;
|
|
992
1101
|
|
|
993
1102
|
// Idempotency: if the destination already exists, short-circuit.
|
|
994
1103
|
if (UEditorAssetLibrary::DoesAssetExist(DestPath))
|
|
@@ -1150,7 +1259,7 @@ static TSharedPtr<FJsonValue> RenameWorldWithExternals(const FString& SourceAsse
|
|
|
1150
1259
|
Gather(ExtActorsSrc, SrcActors);
|
|
1151
1260
|
Gather(ExtObjectsSrc, SrcObjects);
|
|
1152
1261
|
|
|
1153
|
-
UObject* World =
|
|
1262
|
+
UObject* World = MCPLoadAssetObject(SourceAssetPath);
|
|
1154
1263
|
if (!World)
|
|
1155
1264
|
{
|
|
1156
1265
|
return MCPError(FString::Printf(TEXT("Failed to load World asset: %s. No changes made."), *SourceAssetPath));
|
|
@@ -1466,8 +1575,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::RenameAsset(const TSharedPtr<FJsonObject>
|
|
|
1466
1575
|
return MCPError(TEXT("Missing 'sourcePath'+'destinationPath' or 'assetPath'+'newName'"));
|
|
1467
1576
|
}
|
|
1468
1577
|
|
|
1469
|
-
if (MCPIsProtectedAssetPath(SourcePath)) return
|
|
1470
|
-
if (MCPIsProtectedAssetPath(DestPath)) return
|
|
1578
|
+
if (MCPIsProtectedAssetPath(SourcePath)) return MCPProtectedPathError(SourcePath);
|
|
1579
|
+
if (MCPIsProtectedAssetPath(DestPath)) return MCPProtectedPathError(DestPath);
|
|
1471
1580
|
|
|
1472
1581
|
// Idempotency: if already at destination, no-op.
|
|
1473
1582
|
if (SourcePath == DestPath)
|
|
@@ -1561,7 +1670,7 @@ namespace
|
|
|
1561
1670
|
UAssetEditorSubsystem* AES = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
|
1562
1671
|
if (!AES) return false;
|
|
1563
1672
|
|
|
1564
|
-
UObject* Asset =
|
|
1673
|
+
UObject* Asset = MCPLoadAssetObject(AssetPath);
|
|
1565
1674
|
if (!Asset) return false;
|
|
1566
1675
|
|
|
1567
1676
|
const TArray<IAssetEditorInstance*> Editors = AES->FindEditorsForAsset(Asset);
|
|
@@ -1573,6 +1682,89 @@ namespace
|
|
|
1573
1682
|
return true;
|
|
1574
1683
|
}
|
|
1575
1684
|
|
|
1685
|
+
// #976: UEditorAssetLibrary::DeleteAsset is the FORCE-delete entry point.
|
|
1686
|
+
// Its own header says so: "It doesn't check if the asset has references in
|
|
1687
|
+
// other Levels or by Actors." Both delete handlers called it whatever the
|
|
1688
|
+
// caller passed for `force`, so a referenced asset was destroyed and its
|
|
1689
|
+
// referencers were rewritten to point at nothing, with `force=false`
|
|
1690
|
+
// reading as a safety flag that did not exist.
|
|
1691
|
+
//
|
|
1692
|
+
// The Asset Registry holds the same reference graph the Content Browser's
|
|
1693
|
+
// reference viewer draws, so asking it first is what turns a force delete
|
|
1694
|
+
// back into a checked one. Self-references are dropped: a package always
|
|
1695
|
+
// lists itself.
|
|
1696
|
+
TArray<FString> CollectPackageReferencers(const FString& AssetPath)
|
|
1697
|
+
{
|
|
1698
|
+
TArray<FString> Out;
|
|
1699
|
+
|
|
1700
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(AssetPath);
|
|
1701
|
+
if (Forms.PackagePath.IsEmpty()) return Out;
|
|
1702
|
+
|
|
1703
|
+
FAssetRegistryModule& ARM = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
1704
|
+
const FName PackageFName(*Forms.PackagePath);
|
|
1705
|
+
TArray<FName> Refs;
|
|
1706
|
+
ARM.Get().GetReferencers(PackageFName, Refs);
|
|
1707
|
+
for (const FName& R : Refs)
|
|
1708
|
+
{
|
|
1709
|
+
if (R != PackageFName)
|
|
1710
|
+
{
|
|
1711
|
+
Out.AddUnique(R.ToString());
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
Out.Sort([](const FString& A, const FString& B) { return A < B; });
|
|
1715
|
+
return Out;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/** The refusal a checked delete hands back. Names the packages that would
|
|
1719
|
+
* have been rewritten, so the caller can decide rather than discover. */
|
|
1720
|
+
void ApplyReferencerRefusalToJson(
|
|
1721
|
+
const TSharedPtr<FJsonObject>& Out,
|
|
1722
|
+
const FString& AssetPath,
|
|
1723
|
+
const TArray<FString>& Referencers)
|
|
1724
|
+
{
|
|
1725
|
+
// Long lists are the interesting case and truncating them would hide
|
|
1726
|
+
// exactly the referencer a caller is looking for, so the count is
|
|
1727
|
+
// reported alongside a bounded sample rather than a silent slice.
|
|
1728
|
+
constexpr int32 MaxNamed = 25;
|
|
1729
|
+
const int32 Named = FMath::Min(Referencers.Num(), MaxNamed);
|
|
1730
|
+
|
|
1731
|
+
TArray<FString> Sample;
|
|
1732
|
+
Sample.Append(Referencers.GetData(), Named);
|
|
1733
|
+
|
|
1734
|
+
Out->SetStringField(TEXT("error"), FString::Printf(
|
|
1735
|
+
TEXT("'%s' is referenced by %d package(s) and force=false, so it was not deleted. ")
|
|
1736
|
+
TEXT("Deleting it would rewrite those referencers to point at nothing. ")
|
|
1737
|
+
TEXT("Repoint or delete the referencers first, or pass force=true to delete anyway. Referencers: %s%s"),
|
|
1738
|
+
*AssetPath,
|
|
1739
|
+
Referencers.Num(),
|
|
1740
|
+
*FString::Join(Sample, TEXT(", ")),
|
|
1741
|
+
Referencers.Num() > Named ? TEXT(", ...") : TEXT("")));
|
|
1742
|
+
Out->SetStringField(TEXT("path"), AssetPath);
|
|
1743
|
+
Out->SetBoolField(TEXT("deleted"), false);
|
|
1744
|
+
Out->SetStringField(TEXT("reason"), TEXT("has_referencers"));
|
|
1745
|
+
Out->SetNumberField(TEXT("referencerCount"), Referencers.Num());
|
|
1746
|
+
|
|
1747
|
+
TArray<TSharedPtr<FJsonValue>> RefsJson;
|
|
1748
|
+
for (const FString& R : Referencers)
|
|
1749
|
+
{
|
|
1750
|
+
RefsJson.Add(MakeShared<FJsonValueString>(R));
|
|
1751
|
+
}
|
|
1752
|
+
Out->SetArrayField(TEXT("referencers"), RefsJson);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/** Run the checked-delete gate for one path. Returns true and fills Out
|
|
1756
|
+
* with the refusal when the asset has referencers; false when the delete
|
|
1757
|
+
* may proceed. Both delete handlers go through this so the single and
|
|
1758
|
+
* batch forms cannot drift into enforcing different rules. */
|
|
1759
|
+
bool CheckedDeleteRefusal(const FString& AssetPath, const TSharedPtr<FJsonObject>& Out)
|
|
1760
|
+
{
|
|
1761
|
+
const TArray<FString> Referencers = CollectPackageReferencers(AssetPath);
|
|
1762
|
+
if (Referencers.IsEmpty()) return false;
|
|
1763
|
+
Out->SetStringField(TEXT("status"), TEXT("refused"));
|
|
1764
|
+
ApplyReferencerRefusalToJson(Out, AssetPath, Referencers);
|
|
1765
|
+
return true;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1576
1768
|
FDeleteDiagnostics DiagnoseDeleteFailure(const FString& AssetPath)
|
|
1577
1769
|
{
|
|
1578
1770
|
FDeleteDiagnostics Diag;
|
|
@@ -1581,7 +1773,7 @@ namespace
|
|
|
1581
1773
|
{
|
|
1582
1774
|
if (UAssetEditorSubsystem* AES = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>())
|
|
1583
1775
|
{
|
|
1584
|
-
if (UObject* Asset =
|
|
1776
|
+
if (UObject* Asset = MCPLoadAssetObject(AssetPath))
|
|
1585
1777
|
{
|
|
1586
1778
|
Diag.bOpenInEditor = AES->FindEditorsForAsset(Asset).Num() > 0;
|
|
1587
1779
|
}
|
|
@@ -1604,7 +1796,7 @@ namespace
|
|
|
1604
1796
|
// #601: when there are no editors/on-disk referencers the delete still
|
|
1605
1797
|
// fails for non-obvious reasons. Gather the common culprits so callers
|
|
1606
1798
|
// get something actionable instead of a bare "unknown".
|
|
1607
|
-
if (UObject* Asset =
|
|
1799
|
+
if (UObject* Asset = MCPLoadAssetObject(AssetPath))
|
|
1608
1800
|
{
|
|
1609
1801
|
// Live (in-memory) references beyond the asset's own package.
|
|
1610
1802
|
FReferencerInformationList RefInfo;
|
|
@@ -1675,7 +1867,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAsset(const TSharedPtr<FJsonObject>
|
|
|
1675
1867
|
FString AssetPath;
|
|
1676
1868
|
if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
|
|
1677
1869
|
|
|
1678
|
-
if (MCPIsProtectedAssetPath(AssetPath)) return
|
|
1870
|
+
if (MCPIsProtectedAssetPath(AssetPath)) return MCPProtectedPathError(AssetPath);
|
|
1679
1871
|
|
|
1680
1872
|
const bool bForce = OptionalBool(Params, TEXT("force"), false);
|
|
1681
1873
|
|
|
@@ -1688,6 +1880,18 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAsset(const TSharedPtr<FJsonObject>
|
|
|
1688
1880
|
return MCPResult(Result);
|
|
1689
1881
|
}
|
|
1690
1882
|
|
|
1883
|
+
// #976: the delete below is a force delete, so the reference check has to
|
|
1884
|
+
// happen here rather than being left to an API that does not do one.
|
|
1885
|
+
if (!bForce)
|
|
1886
|
+
{
|
|
1887
|
+
TSharedPtr<FJsonObject> Refused = MakeShared<FJsonObject>();
|
|
1888
|
+
Refused->SetBoolField(TEXT("success"), false);
|
|
1889
|
+
if (CheckedDeleteRefusal(AssetPath, Refused))
|
|
1890
|
+
{
|
|
1891
|
+
return MCPResult(Refused);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1691
1895
|
bool bClosedEditor = false;
|
|
1692
1896
|
if (bForce)
|
|
1693
1897
|
{
|
|
@@ -1699,6 +1903,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAsset(const TSharedPtr<FJsonObject>
|
|
|
1699
1903
|
auto Result = MCPSuccess();
|
|
1700
1904
|
Result->SetStringField(TEXT("path"), AssetPath);
|
|
1701
1905
|
Result->SetBoolField(TEXT("deleted"), bSuccess);
|
|
1906
|
+
Result->SetBoolField(TEXT("forced"), bForce);
|
|
1702
1907
|
if (bClosedEditor)
|
|
1703
1908
|
{
|
|
1704
1909
|
Result->SetBoolField(TEXT("closedOpenEditor"), true);
|
|
@@ -1727,6 +1932,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAssetBatch(const TSharedPtr<FJsonOb
|
|
|
1727
1932
|
int32 Deleted = 0;
|
|
1728
1933
|
int32 Absent = 0;
|
|
1729
1934
|
int32 Failed = 0;
|
|
1935
|
+
int32 Refused = 0;
|
|
1730
1936
|
int32 ClosedEditors = 0;
|
|
1731
1937
|
|
|
1732
1938
|
int32 Protected = 0;
|
|
@@ -1752,6 +1958,15 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAssetBatch(const TSharedPtr<FJsonOb
|
|
|
1752
1958
|
Entry->SetStringField(TEXT("status"), TEXT("absent"));
|
|
1753
1959
|
Absent++;
|
|
1754
1960
|
}
|
|
1961
|
+
else if (!bForce && CheckedDeleteRefusal(Path, Entry))
|
|
1962
|
+
{
|
|
1963
|
+
// #976: same rule as the single delete. Per-asset, because one
|
|
1964
|
+
// referenced entry in a batch is not a reason to refuse the rest,
|
|
1965
|
+
// and a bare "failed" count never said which entry was skipped or
|
|
1966
|
+
// why. Status is its own value so a caller can tell a refusal
|
|
1967
|
+
// apart from a delete the editor attempted and could not finish.
|
|
1968
|
+
Refused++;
|
|
1969
|
+
}
|
|
1755
1970
|
else
|
|
1756
1971
|
{
|
|
1757
1972
|
bool bClosed = false;
|
|
@@ -1781,6 +1996,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAssetBatch(const TSharedPtr<FJsonOb
|
|
|
1781
1996
|
Result->SetNumberField(TEXT("deleted"), Deleted);
|
|
1782
1997
|
Result->SetNumberField(TEXT("absent"), Absent);
|
|
1783
1998
|
Result->SetNumberField(TEXT("failed"), Failed);
|
|
1999
|
+
Result->SetNumberField(TEXT("refused"), Refused);
|
|
2000
|
+
Result->SetBoolField(TEXT("forced"), bForce);
|
|
1784
2001
|
if (Protected > 0) Result->SetNumberField(TEXT("protected"), Protected);
|
|
1785
2002
|
Result->SetNumberField(TEXT("total"), PerPath.Num());
|
|
1786
2003
|
if (ClosedEditors > 0) Result->SetNumberField(TEXT("closedEditors"), ClosedEditors);
|
|
@@ -1891,7 +2108,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRename(const TSharedPtr<FJsonObject>&
|
|
|
1891
2108
|
continue;
|
|
1892
2109
|
}
|
|
1893
2110
|
|
|
1894
|
-
UObject* Asset =
|
|
2111
|
+
UObject* Asset = MCPLoadAssetObject(SourcePath);
|
|
1895
2112
|
if (!Asset)
|
|
1896
2113
|
{
|
|
1897
2114
|
Record->SetStringField(TEXT("status"), TEXT("not_found"));
|
|
@@ -1948,6 +2165,48 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRename(const TSharedPtr<FJsonObject>&
|
|
|
1948
2165
|
}
|
|
1949
2166
|
}
|
|
1950
2167
|
|
|
2168
|
+
// #908: RenameAssets runs one redirector fix-up pass, and a referencer that
|
|
2169
|
+
// is not loaded is invisible to it, so a stub is left at the old path with
|
|
2170
|
+
// nothing in the response saying so. 29 renames left 19 of them, and the
|
|
2171
|
+
// first sign of it was a later search finding redirectors nobody expected.
|
|
2172
|
+
IAssetRegistry& RenameRegistry =
|
|
2173
|
+
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
|
2174
|
+
int32 RedirectorsLeft = 0;
|
|
2175
|
+
TArray<TSharedPtr<FJsonValue>> RedirectorPackages;
|
|
2176
|
+
for (const TSharedPtr<FJsonValue>& Value : PerItem)
|
|
2177
|
+
{
|
|
2178
|
+
TSharedPtr<FJsonObject> Rec = Value.IsValid() ? Value->AsObject() : nullptr;
|
|
2179
|
+
if (!Rec.IsValid()) continue;
|
|
2180
|
+
FString Status;
|
|
2181
|
+
Rec->TryGetStringField(TEXT("status"), Status);
|
|
2182
|
+
if (Status != TEXT("renamed")) continue;
|
|
2183
|
+
|
|
2184
|
+
FString SourcePath;
|
|
2185
|
+
Rec->TryGetStringField(TEXT("sourcePath"), SourcePath);
|
|
2186
|
+
const FMCPAssetPathForms SourceForms = MCPAssetPathForms(SourcePath);
|
|
2187
|
+
|
|
2188
|
+
bool bRedirectorLeft = false;
|
|
2189
|
+
TArray<FAssetData> AtOldPath;
|
|
2190
|
+
RenameRegistry.GetAssetsByPackageName(FName(*SourceForms.PackagePath), AtOldPath);
|
|
2191
|
+
for (const FAssetData& Data : AtOldPath)
|
|
2192
|
+
{
|
|
2193
|
+
if (Data.IsRedirector()) { bRedirectorLeft = true; break; }
|
|
2194
|
+
}
|
|
2195
|
+
// The registry can lag a rename that just happened, so an in-memory
|
|
2196
|
+
// redirector counts too rather than reading as a clean result.
|
|
2197
|
+
if (!bRedirectorLeft && FindObject<UObjectRedirector>(nullptr, *SourceForms.ObjectPath))
|
|
2198
|
+
{
|
|
2199
|
+
bRedirectorLeft = true;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
Rec->SetBoolField(TEXT("redirectorLeft"), bRedirectorLeft);
|
|
2203
|
+
if (bRedirectorLeft)
|
|
2204
|
+
{
|
|
2205
|
+
++RedirectorsLeft;
|
|
2206
|
+
RedirectorPackages.Add(MakeShared<FJsonValueString>(SourceForms.PackagePath));
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
|
|
1951
2210
|
auto Result = MCPSuccess();
|
|
1952
2211
|
if (Succeeded > 0) MCPSetUpdated(Result);
|
|
1953
2212
|
Result->SetBoolField(TEXT("success"), bOk);
|
|
@@ -1956,6 +2215,16 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRename(const TSharedPtr<FJsonObject>&
|
|
|
1956
2215
|
Result->SetNumberField(TEXT("skipped"), Skipped);
|
|
1957
2216
|
Result->SetNumberField(TEXT("total"), PerItem.Num());
|
|
1958
2217
|
Result->SetArrayField(TEXT("results"), PerItem);
|
|
2218
|
+
Result->SetNumberField(TEXT("redirectorsLeft"), RedirectorsLeft);
|
|
2219
|
+
Result->SetNumberField(TEXT("redirectorsRemoved"), FMath::Max(0, Succeeded - RedirectorsLeft));
|
|
2220
|
+
Result->SetArrayField(TEXT("redirectorPackages"), RedirectorPackages);
|
|
2221
|
+
if (RedirectorsLeft > 0)
|
|
2222
|
+
{
|
|
2223
|
+
Result->SetStringField(TEXT("note"), FString::Printf(
|
|
2224
|
+
TEXT("%d of %d renamed assets left an ObjectRedirector at the old path, still pointed at by packages this rename did not load. ")
|
|
2225
|
+
TEXT("Pass redirectorPackages to asset(fixup_redirectors) to load exactly those referencers, rewrite them, and delete the stubs that come out unreferenced."),
|
|
2226
|
+
RedirectorsLeft, Succeeded));
|
|
2227
|
+
}
|
|
1959
2228
|
return MCPResult(Result);
|
|
1960
2229
|
}
|
|
1961
2230
|
|
|
@@ -2160,7 +2429,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::SaveAsset(const TSharedPtr<FJsonObject>&
|
|
|
2160
2429
|
UObject* Asset = LoadAssetByPath<UObject>(AssetPath);
|
|
2161
2430
|
if (!Asset)
|
|
2162
2431
|
{
|
|
2163
|
-
return
|
|
2432
|
+
return MCPAssetNotFoundError(AssetPath);
|
|
2164
2433
|
}
|
|
2165
2434
|
UPackage* Package = Asset->GetOutermost();
|
|
2166
2435
|
if (!Package)
|
|
@@ -2175,7 +2444,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::SaveAsset(const TSharedPtr<FJsonObject>&
|
|
|
2175
2444
|
else
|
|
2176
2445
|
{
|
|
2177
2446
|
bSuccess = UEditorAssetLibrary::SaveAsset(AssetPath);
|
|
2178
|
-
if (UObject* Asset =
|
|
2447
|
+
if (UObject* Asset = MCPLoadAssetObject(AssetPath))
|
|
2179
2448
|
{
|
|
2180
2449
|
if (UPackage* Package = Asset->GetOutermost())
|
|
2181
2450
|
{
|
|
@@ -2287,11 +2556,9 @@ TSharedPtr<FJsonValue> FAssetHandlers::ReloadPackage(const TSharedPtr<FJsonObjec
|
|
|
2287
2556
|
FString AssetPath;
|
|
2288
2557
|
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
2289
2558
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
return MCPError(FString::Printf(TEXT("Asset not found: %s"), *AssetPath));
|
|
2294
|
-
}
|
|
2559
|
+
TSharedPtr<FJsonValue> LoadError;
|
|
2560
|
+
UObject* Asset = MCPRequireAssetObject(AssetPath, LoadError);
|
|
2561
|
+
if (!Asset) return LoadError;
|
|
2295
2562
|
|
|
2296
2563
|
UPackage* Package = Asset->GetOutermost();
|
|
2297
2564
|
if (!Package)
|
|
@@ -2312,7 +2579,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ReloadPackage(const TSharedPtr<FJsonObjec
|
|
|
2312
2579
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
|
|
2313
2580
|
|
|
2314
2581
|
// Reload
|
|
2315
|
-
UObject* Reloaded =
|
|
2582
|
+
UObject* Reloaded = MCPLoadAssetObject(AssetPath);
|
|
2316
2583
|
bSuccess = (Reloaded != nullptr);
|
|
2317
2584
|
}
|
|
2318
2585
|
|
|
@@ -2533,8 +2800,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::MoveFolder(const TSharedPtr<FJsonObject>&
|
|
|
2533
2800
|
SourcePath.RemoveFromEnd(TEXT("/"));
|
|
2534
2801
|
DestinationPath.RemoveFromEnd(TEXT("/"));
|
|
2535
2802
|
|
|
2536
|
-
if (MCPIsProtectedAssetPath(SourcePath)) return
|
|
2537
|
-
if (MCPIsProtectedAssetPath(DestinationPath)) return
|
|
2803
|
+
if (MCPIsProtectedAssetPath(SourcePath)) return MCPProtectedPathError(SourcePath);
|
|
2804
|
+
if (MCPIsProtectedAssetPath(DestinationPath)) return MCPProtectedPathError(DestinationPath);
|
|
2538
2805
|
|
|
2539
2806
|
// Scan source path to discover all assets
|
|
2540
2807
|
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
|
@@ -2809,7 +3076,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::HealthCheck(const TSharedPtr<FJsonObject>
|
|
|
2809
3076
|
bool bCanLoad = bIsLoaded;
|
|
2810
3077
|
if (!bIsLoaded)
|
|
2811
3078
|
{
|
|
2812
|
-
UObject* Probe =
|
|
3079
|
+
UObject* Probe = MCPLoadAssetObject(AssetPath);
|
|
2813
3080
|
bCanLoad = Probe != nullptr;
|
|
2814
3081
|
if (Probe) InMemory = Probe;
|
|
2815
3082
|
}
|
|
@@ -2838,7 +3105,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::ForceReload(const TSharedPtr<FJsonObject>
|
|
|
2838
3105
|
FString AssetPath;
|
|
2839
3106
|
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
2840
3107
|
|
|
2841
|
-
const
|
|
3108
|
+
const FMCPAssetPathForms Forms = MCPAssetPathForms(AssetPath);
|
|
3109
|
+
const FString PackageName = Forms.PackagePath;
|
|
2842
3110
|
FString PackageFileName;
|
|
2843
3111
|
if (!FPackageName::DoesPackageExist(PackageName, &PackageFileName))
|
|
2844
3112
|
{
|
|
@@ -2851,7 +3119,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ForceReload(const TSharedPtr<FJsonObject>
|
|
|
2851
3119
|
{
|
|
2852
3120
|
if (UAssetEditorSubsystem* AES = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>())
|
|
2853
3121
|
{
|
|
2854
|
-
if (UObject* Existing = StaticFindObject(UObject::StaticClass(), nullptr, *
|
|
3122
|
+
if (UObject* Existing = StaticFindObject(UObject::StaticClass(), nullptr, *Forms.ObjectPath))
|
|
2855
3123
|
{
|
|
2856
3124
|
if (AES->FindEditorsForAsset(Existing).Num() > 0)
|
|
2857
3125
|
{
|
|
@@ -2869,7 +3137,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ForceReload(const TSharedPtr<FJsonObject>
|
|
|
2869
3137
|
// package, recompiles the Blueprint and reinstances against the new class.
|
|
2870
3138
|
// A package the editor refuses to release is reported, not papered over.
|
|
2871
3139
|
UPackage* ExistingPkg = FindPackage(nullptr, *PackageName);
|
|
2872
|
-
UObject* PreviousObject = StaticFindObject(UObject::StaticClass(), nullptr, *
|
|
3140
|
+
UObject* PreviousObject = StaticFindObject(UObject::StaticClass(), nullptr, *Forms.ObjectPath);
|
|
2873
3141
|
const TWeakObjectPtr<UObject> PreviousWeak(PreviousObject);
|
|
2874
3142
|
const bool bWasDirty = ExistingPkg != nullptr && ExistingPkg->IsDirty();
|
|
2875
3143
|
const bool bDiscardUnsaved = OptionalBool(Params, TEXT("discardUnsaved"), false);
|
|
@@ -2913,7 +3181,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::ForceReload(const TSharedPtr<FJsonObject>
|
|
|
2913
3181
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
|
|
2914
3182
|
}
|
|
2915
3183
|
|
|
2916
|
-
UObject* Reloaded = LoadObject<UObject>(nullptr, *
|
|
3184
|
+
UObject* Reloaded = LoadObject<UObject>(nullptr, *Forms.ObjectPath, nullptr, LOAD_None);
|
|
2917
3185
|
const bool bSuccess = Reloaded != nullptr;
|
|
2918
3186
|
// A package that was already in memory must come back as a new object for
|
|
2919
3187
|
// the read to be trustworthy. Same pointer means the editor kept its copy.
|
|
@@ -2968,12 +3236,15 @@ TSharedPtr<FJsonValue> FAssetHandlers::SetAssetProperty(const TSharedPtr<FJsonOb
|
|
|
2968
3236
|
return MCPError(TEXT("Missing 'value' parameter"));
|
|
2969
3237
|
}
|
|
2970
3238
|
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
Asset =
|
|
3239
|
+
// #931: default to persisting. A write that only marked the package dirty
|
|
3240
|
+
// was the whole defect, so opting out has to be deliberate.
|
|
3241
|
+
const bool bSave = OptionalBool(Params, TEXT("save"), true);
|
|
3242
|
+
|
|
3243
|
+
TSharedPtr<FJsonValue> LoadError;
|
|
3244
|
+
UObject* Asset = MCPRequireAssetObject(AssetPath, LoadError);
|
|
3245
|
+
if (!Asset) return LoadError;
|
|
3246
|
+
UBlueprint* OwningBlueprint = nullptr;
|
|
3247
|
+
Asset = MCPResolveAssetToCDO(Asset, &OwningBlueprint); // #568 - author the generated-class CDO for Blueprint paths
|
|
2977
3248
|
|
|
2978
3249
|
// Resolve the (possibly indexed, possibly subobject-descending) path.
|
|
2979
3250
|
// Supports "Config.Traits[1].Params.RepresentationActorManagementClass"
|
|
@@ -3010,16 +3281,21 @@ TSharedPtr<FJsonValue> FAssetHandlers::SetAssetProperty(const TSharedPtr<FJsonOb
|
|
|
3010
3281
|
if (LeafOwner) LeafOwner->PostEditChange();
|
|
3011
3282
|
Asset->PostEditChange();
|
|
3012
3283
|
Asset->MarkPackageDirty();
|
|
3284
|
+
MCPNoteBlueprintCDOWrite(OwningBlueprint, PropertyName);
|
|
3013
3285
|
|
|
3014
3286
|
FString NewValue;
|
|
3015
3287
|
FinalProp->ExportText_Direct(NewValue, ValuePtr, ValuePtr, nullptr, PPF_None);
|
|
3016
3288
|
|
|
3289
|
+
FString PersistReason;
|
|
3290
|
+
const bool bPersisted = MCPPersistAssetWrite(Asset, OwningBlueprint, bSave, PersistReason);
|
|
3291
|
+
|
|
3017
3292
|
auto Result = MCPSuccess();
|
|
3018
3293
|
MCPSetUpdated(Result);
|
|
3019
3294
|
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
|
3020
3295
|
Result->SetStringField(TEXT("propertyName"), PropertyName);
|
|
3021
3296
|
Result->SetStringField(TEXT("previousValue"), PrevValue);
|
|
3022
3297
|
Result->SetStringField(TEXT("value"), NewValue);
|
|
3298
|
+
MCPDescribePropertyWritePersistence(Result, Asset, PropertyName, bSave, bPersisted, PersistReason);
|
|
3023
3299
|
if (bMapBearing)
|
|
3024
3300
|
{
|
|
3025
3301
|
Result->SetNumberField(TEXT("mapPairCount"), MCPPropertyText::CountMapPairs(FinalProp, ValuePtr));
|
|
@@ -3061,12 +3337,13 @@ TSharedPtr<FJsonValue> FAssetHandlers::AppendAssetArrayElements(const TSharedPtr
|
|
|
3061
3337
|
return MCPError(TEXT("'elements' must contain at least one value"));
|
|
3062
3338
|
}
|
|
3063
3339
|
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3340
|
+
const bool bSave = OptionalBool(Params, TEXT("save"), true);
|
|
3341
|
+
|
|
3342
|
+
TSharedPtr<FJsonValue> LoadError;
|
|
3343
|
+
UObject* Asset = MCPRequireAssetObject(AssetPath, LoadError);
|
|
3344
|
+
if (!Asset) return LoadError;
|
|
3345
|
+
UBlueprint* OwningBlueprint = nullptr;
|
|
3346
|
+
Asset = MCPResolveAssetToCDO(Asset, &OwningBlueprint);
|
|
3070
3347
|
|
|
3071
3348
|
FProperty* FinalProp = nullptr;
|
|
3072
3349
|
void* ValuePtr = nullptr;
|
|
@@ -3139,6 +3416,10 @@ TSharedPtr<FJsonValue> FAssetHandlers::AppendAssetArrayElements(const TSharedPtr
|
|
|
3139
3416
|
if (LeafOwner && LeafOwner != Asset) LeafOwner->PostEditChange();
|
|
3140
3417
|
Asset->PostEditChange();
|
|
3141
3418
|
Asset->MarkPackageDirty();
|
|
3419
|
+
MCPNoteBlueprintCDOWrite(OwningBlueprint, PropertyName);
|
|
3420
|
+
|
|
3421
|
+
FString PersistReason;
|
|
3422
|
+
const bool bPersisted = MCPPersistAssetWrite(Asset, OwningBlueprint, bSave, PersistReason);
|
|
3142
3423
|
|
|
3143
3424
|
TArray<TSharedPtr<FJsonValue>> AppendedIndices;
|
|
3144
3425
|
AppendedIndices.Reserve(AppendedCount);
|
|
@@ -3157,7 +3438,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::AppendAssetArrayElements(const TSharedPtr
|
|
|
3157
3438
|
Result->SetNumberField(TEXT("newNum"), PreviousNum + AppendedCount);
|
|
3158
3439
|
Result->SetArrayField(TEXT("appendedIndices"), AppendedIndices);
|
|
3159
3440
|
Result->SetField(TEXT("previousValue"), PreviousValue);
|
|
3160
|
-
Result
|
|
3441
|
+
MCPDescribePropertyWritePersistence(Result, Asset, PropertyName, bSave, bPersisted, PersistReason);
|
|
3161
3442
|
|
|
3162
3443
|
TSharedPtr<FJsonObject> Payload = MakeShared<FJsonObject>();
|
|
3163
3444
|
Payload->SetStringField(TEXT("assetPath"), AssetPath);
|
|
@@ -3221,7 +3502,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::SetTextureSettingsByType(const TSharedPtr
|
|
|
3221
3502
|
{
|
|
3222
3503
|
FString TexPath;
|
|
3223
3504
|
if (!V->TryGetString(TexPath)) continue;
|
|
3224
|
-
UTexture2D* Tex =
|
|
3505
|
+
UTexture2D* Tex = LoadAssetByPath<UTexture2D>(TexPath);
|
|
3225
3506
|
if (!Tex)
|
|
3226
3507
|
{
|
|
3227
3508
|
TSharedPtr<FJsonObject> F = MakeShared<FJsonObject>();
|
|
@@ -3453,11 +3734,9 @@ TSharedPtr<FJsonValue> FAssetHandlers::MigrateAssets(const TSharedPtr<FJsonObjec
|
|
|
3453
3734
|
TArray<TSharedPtr<FJsonValue>> Resolved;
|
|
3454
3735
|
for (const FString& Path : AssetPaths)
|
|
3455
3736
|
{
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
return MCPError(FString::Printf(TEXT("Asset not found: %s"), *Path));
|
|
3460
|
-
}
|
|
3737
|
+
TSharedPtr<FJsonValue> LoadError;
|
|
3738
|
+
UObject* Asset = MCPRequireAssetObject(Path, LoadError);
|
|
3739
|
+
if (!Asset) return LoadError;
|
|
3461
3740
|
UPackage* Package = Asset->GetOutermost();
|
|
3462
3741
|
const FName PackageName(*Package->GetName());
|
|
3463
3742
|
|