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,467 @@
|
|
|
1
|
+
#include "MassHandlers.h"
|
|
2
|
+
|
|
3
|
+
#include "HandlerAssetCreate.h"
|
|
4
|
+
#include "HandlerJsonProperty.h"
|
|
5
|
+
#include "HandlerRegistry.h"
|
|
6
|
+
#include "HandlerUtils.h"
|
|
7
|
+
|
|
8
|
+
#include "AssetRegistry/AssetRegistryModule.h"
|
|
9
|
+
#include "Editor.h"
|
|
10
|
+
#include "EditorScriptingUtilities/Public/EditorAssetLibrary.h"
|
|
11
|
+
#include "Engine/DataAsset.h"
|
|
12
|
+
#include "Misc/PackageName.h"
|
|
13
|
+
#include "ScopedTransaction.h"
|
|
14
|
+
#include "Misc/Paths.h"
|
|
15
|
+
#include "UObject/UObjectGlobals.h"
|
|
16
|
+
#include "UObject/UnrealType.h"
|
|
17
|
+
|
|
18
|
+
namespace
|
|
19
|
+
{
|
|
20
|
+
static const TCHAR* ConfigClassPath = TEXT("/Script/MassSpawner.MassEntityConfigAsset");
|
|
21
|
+
static const TCHAR* TraitBaseClassPath = TEXT("/Script/MassSpawner.MassEntityTraitBase");
|
|
22
|
+
|
|
23
|
+
UClass* ResolveClass(const FString& ClassPath)
|
|
24
|
+
{
|
|
25
|
+
if (ClassPath.IsEmpty()) return nullptr;
|
|
26
|
+
UClass* Result = LoadClass<UObject>(nullptr, *ClassPath);
|
|
27
|
+
if (!Result) Result = LoadObject<UClass>(nullptr, *ClassPath);
|
|
28
|
+
if (!Result)
|
|
29
|
+
{
|
|
30
|
+
FString ShortName = ClassPath;
|
|
31
|
+
ShortName.RemoveFromEnd(TEXT("_C"));
|
|
32
|
+
for (TObjectIterator<UClass> It; It; ++It)
|
|
33
|
+
{
|
|
34
|
+
if (It->GetName() == ShortName || It->GetName() == FPackageName::GetShortName(ClassPath))
|
|
35
|
+
{
|
|
36
|
+
Result = *It;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return Result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
bool SplitAssetPath(const TSharedPtr<FJsonObject>& Params, FString& OutName, FString& OutPackagePath)
|
|
45
|
+
{
|
|
46
|
+
FString AssetPath;
|
|
47
|
+
Params->TryGetStringField(TEXT("assetPath"), AssetPath);
|
|
48
|
+
if (AssetPath.IsEmpty())
|
|
49
|
+
{
|
|
50
|
+
if (!Params->TryGetStringField(TEXT("name"), OutName) || OutName.IsEmpty()) return false;
|
|
51
|
+
OutPackagePath = OptionalString(Params, TEXT("packagePath"), TEXT("/Game"));
|
|
52
|
+
return !OutPackagePath.IsEmpty();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
FString PackageName = AssetPath;
|
|
56
|
+
if (AssetPath.Contains(TEXT(".")))
|
|
57
|
+
{
|
|
58
|
+
PackageName = AssetPath.LeftChop(AssetPath.Len() - AssetPath.Find(TEXT("."), ESearchCase::CaseSensitive, ESearchDir::FromEnd));
|
|
59
|
+
}
|
|
60
|
+
OutName = FPackageName::GetShortName(PackageName);
|
|
61
|
+
OutPackagePath = FPaths::GetPath(PackageName);
|
|
62
|
+
return !OutName.IsEmpty() && !OutPackagePath.IsEmpty();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
UObject* LoadConfigAsset(const FString& Name, const FString& PackagePath)
|
|
66
|
+
{
|
|
67
|
+
return LoadObject<UObject>(nullptr, *(PackagePath + TEXT("/") + Name + TEXT(".") + Name));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
FProperty* FindTraitsProperty(UObject* ConfigAsset, void*& OutTraitsAddress)
|
|
71
|
+
{
|
|
72
|
+
OutTraitsAddress = nullptr;
|
|
73
|
+
if (!ConfigAsset) return nullptr;
|
|
74
|
+
FStructProperty* ConfigProperty = CastField<FStructProperty>(ConfigAsset->GetClass()->FindPropertyByName(TEXT("Config")));
|
|
75
|
+
if (!ConfigProperty) return nullptr;
|
|
76
|
+
void* ConfigAddress = ConfigProperty->ContainerPtrToValuePtr<void>(ConfigAsset);
|
|
77
|
+
FArrayProperty* TraitsProperty = CastField<FArrayProperty>(ConfigProperty->Struct->FindPropertyByName(TEXT("Traits")));
|
|
78
|
+
if (!TraitsProperty) return nullptr;
|
|
79
|
+
OutTraitsAddress = TraitsProperty->ContainerPtrToValuePtr<void>(ConfigAddress);
|
|
80
|
+
return TraitsProperty;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
bool ReadTraitClasses(UObject* ConfigAsset, TArray<UClass*>& OutClasses, FString& OutError)
|
|
84
|
+
{
|
|
85
|
+
void* TraitsAddress = nullptr;
|
|
86
|
+
FArrayProperty* TraitsProperty = CastField<FArrayProperty>(FindTraitsProperty(ConfigAsset, TraitsAddress));
|
|
87
|
+
if (!TraitsProperty) { OutError = TEXT("Mass entity config does not expose Config.Traits"); return false; }
|
|
88
|
+
FObjectProperty* TraitObjectProperty = CastField<FObjectProperty>(TraitsProperty->Inner);
|
|
89
|
+
if (!TraitObjectProperty) { OutError = TEXT("Mass entity config Traits is not an object array"); return false; }
|
|
90
|
+
|
|
91
|
+
FScriptArrayHelper Helper(TraitsProperty, TraitsAddress);
|
|
92
|
+
OutClasses.Reserve(Helper.Num());
|
|
93
|
+
for (int32 Index = 0; Index < Helper.Num(); ++Index)
|
|
94
|
+
{
|
|
95
|
+
UObject* Trait = TraitObjectProperty->GetObjectPropertyValue(Helper.GetRawPtr(Index));
|
|
96
|
+
if (!Trait) { OutError = FString::Printf(TEXT("Config.Traits[%d] is null"), Index); return false; }
|
|
97
|
+
OutClasses.Add(Trait->GetClass());
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
bool AddTrait(UObject* ConfigAsset, UClass* TraitClass, UObject*& OutTrait, FString& OutError)
|
|
103
|
+
{
|
|
104
|
+
OutTrait = nullptr;
|
|
105
|
+
void* TraitsAddress = nullptr;
|
|
106
|
+
FArrayProperty* TraitsProperty = CastField<FArrayProperty>(FindTraitsProperty(ConfigAsset, TraitsAddress));
|
|
107
|
+
if (!TraitsProperty) { OutError = TEXT("Mass entity config does not expose Config.Traits"); return false; }
|
|
108
|
+
FObjectProperty* TraitObjectProperty = CastField<FObjectProperty>(TraitsProperty->Inner);
|
|
109
|
+
if (!TraitObjectProperty) { OutError = TEXT("Mass entity config Traits is not an object array"); return false; }
|
|
110
|
+
|
|
111
|
+
const FName TraitName = MakeUniqueObjectName(ConfigAsset, TraitClass, TraitClass->GetFName());
|
|
112
|
+
UObject* NewTrait = NewObject<UObject>(ConfigAsset, TraitClass, TraitName, RF_Transactional);
|
|
113
|
+
if (!NewTrait) { OutError = FString::Printf(TEXT("Failed to construct trait %s"), *TraitClass->GetPathName()); return false; }
|
|
114
|
+
|
|
115
|
+
FScriptArrayHelper Helper(TraitsProperty, TraitsAddress);
|
|
116
|
+
const int32 NewIndex = Helper.AddValue();
|
|
117
|
+
TraitObjectProperty->SetObjectPropertyValue(Helper.GetRawPtr(NewIndex), NewTrait);
|
|
118
|
+
OutTrait = NewTrait;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
UObject* GetTraitAtIndex(UObject* ConfigAsset, int32 Index)
|
|
123
|
+
{
|
|
124
|
+
void* TraitsAddress = nullptr;
|
|
125
|
+
FArrayProperty* TraitsProperty = CastField<FArrayProperty>(FindTraitsProperty(ConfigAsset, TraitsAddress));
|
|
126
|
+
FObjectProperty* TraitObjectProperty = TraitsProperty ? CastField<FObjectProperty>(TraitsProperty->Inner) : nullptr;
|
|
127
|
+
if (!TraitsProperty || !TraitObjectProperty || Index < 0) return nullptr;
|
|
128
|
+
FScriptArrayHelper Helper(TraitsProperty, TraitsAddress);
|
|
129
|
+
return Index < Helper.Num() ? TraitObjectProperty->GetObjectPropertyValue(Helper.GetRawPtr(Index)) : nullptr;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
TSharedPtr<FJsonValue> ValidateAndApplyProperties(UObject* Trait, const TSharedPtr<FJsonObject>& Properties, int32 TraitIndex, int32& OutSet)
|
|
133
|
+
{
|
|
134
|
+
if (!Properties.IsValid()) return nullptr;
|
|
135
|
+
for (const auto& Pair : Properties->Values)
|
|
136
|
+
{
|
|
137
|
+
// UE 5.8 stores JSON object keys in shared string storage; copy the
|
|
138
|
+
// key before passing it to the FString-based property-path helper.
|
|
139
|
+
const FString PropertyName(Pair.Key);
|
|
140
|
+
FString Error;
|
|
141
|
+
if (!MCPJsonProperty::SetDottedPropertyFromJson(Trait, PropertyName, Pair.Value, Error))
|
|
142
|
+
{
|
|
143
|
+
return MCPError(FString::Printf(TEXT("Trait %d property '%s' failed: %s"), TraitIndex, *PropertyName, *Error));
|
|
144
|
+
}
|
|
145
|
+
++OutSet;
|
|
146
|
+
}
|
|
147
|
+
return nullptr;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
TSharedPtr<FJsonValue> MakeExistingResult(UObject* Asset, const TArray<UClass*>& ExistingClasses, bool bUpdated, int32 PropertiesSet)
|
|
151
|
+
{
|
|
152
|
+
TSharedPtr<FJsonObject> Result = MCPSuccess();
|
|
153
|
+
MCPSetExisted(Result);
|
|
154
|
+
if (bUpdated) MCPSetUpdated(Result);
|
|
155
|
+
Result->SetStringField(TEXT("assetPath"), Asset->GetPathName());
|
|
156
|
+
Result->SetNumberField(TEXT("traits"), ExistingClasses.Num());
|
|
157
|
+
Result->SetNumberField(TEXT("propertiesSet"), PropertiesSet);
|
|
158
|
+
return MCPResult(Result);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
void ReleasePreviews(TArray<UObject*>& Previews)
|
|
162
|
+
{
|
|
163
|
+
for (UObject* Preview : Previews)
|
|
164
|
+
{
|
|
165
|
+
if (Preview) Preview->RemoveFromRoot();
|
|
166
|
+
}
|
|
167
|
+
Previews.Reset();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
FString ExportTraitProperty(const FProperty* Property, const UObject* Trait)
|
|
171
|
+
{
|
|
172
|
+
if (!Property || !Trait) return TEXT("");
|
|
173
|
+
if (const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(Property))
|
|
174
|
+
{
|
|
175
|
+
if (UObject* Value = ObjectProperty->GetObjectPropertyValue_InContainer(Trait))
|
|
176
|
+
{
|
|
177
|
+
return Value->GetPathName();
|
|
178
|
+
}
|
|
179
|
+
return TEXT("None");
|
|
180
|
+
}
|
|
181
|
+
if (const FSoftObjectProperty* SoftObjectProperty = CastField<FSoftObjectProperty>(Property))
|
|
182
|
+
{
|
|
183
|
+
return SoftObjectProperty->GetPropertyValue_InContainer(Trait).ToSoftObjectPath().ToString();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
FString Exported;
|
|
187
|
+
const void* ValueAddress = Property->ContainerPtrToValuePtr<void>(Trait);
|
|
188
|
+
Property->ExportTextItem_Direct(Exported, ValueAddress, nullptr, nullptr, PPF_None);
|
|
189
|
+
return Exported;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
void FMassHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
193
|
+
{
|
|
194
|
+
Registry.RegisterHandler(TEXT("ensure_mass_entity_config"), &EnsureEntityConfig);
|
|
195
|
+
Registry.RegisterHandler(TEXT("read_mass_entity_config"), &ReadEntityConfig);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
TSharedPtr<FJsonValue> FMassHandlers::ReadEntityConfig(const TSharedPtr<FJsonObject>& Params)
|
|
199
|
+
{
|
|
200
|
+
FString AssetPath;
|
|
201
|
+
if (auto Err = RequireString(Params, TEXT("assetPath"), AssetPath)) return Err;
|
|
202
|
+
|
|
203
|
+
UClass* ConfigClass = ResolveClass(ConfigClassPath);
|
|
204
|
+
UClass* TraitBaseClass = ResolveClass(TraitBaseClassPath);
|
|
205
|
+
if (!ConfigClass || !TraitBaseClass)
|
|
206
|
+
{
|
|
207
|
+
return MCPError(TEXT("MassSpawner is unavailable; enable MassGameplay/MassSpawner before using read_mass_entity_config"));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
UObject* Asset = LoadAssetByPath<UObject>(AssetPath);
|
|
211
|
+
if (!Asset) return MCPError(FString::Printf(TEXT("Mass entity config not found: %s"), *AssetPath));
|
|
212
|
+
if (!Asset->IsA(ConfigClass))
|
|
213
|
+
{
|
|
214
|
+
return MCPError(FString::Printf(TEXT("Asset is not a MassEntityConfigAsset: %s"), *Asset->GetPathName()));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
TArray<UClass*> TraitClasses;
|
|
218
|
+
FString Error;
|
|
219
|
+
if (!ReadTraitClasses(Asset, TraitClasses, Error)) return MCPError(Error);
|
|
220
|
+
|
|
221
|
+
auto Result = MCPSuccess();
|
|
222
|
+
Result->SetStringField(TEXT("assetPath"), Asset->GetPathName());
|
|
223
|
+
Result->SetStringField(TEXT("assetClass"), Asset->GetClass()->GetPathName());
|
|
224
|
+
|
|
225
|
+
TArray<TSharedPtr<FJsonValue>> TraitClassNames;
|
|
226
|
+
TArray<TSharedPtr<FJsonValue>> TraitRecords;
|
|
227
|
+
for (int32 Index = 0; Index < TraitClasses.Num(); ++Index)
|
|
228
|
+
{
|
|
229
|
+
UObject* Trait = GetTraitAtIndex(Asset, Index);
|
|
230
|
+
if (!Trait || !Trait->GetClass()->IsChildOf(TraitBaseClass))
|
|
231
|
+
{
|
|
232
|
+
return MCPError(FString::Printf(TEXT("Config.Traits[%d] is not a UMassEntityTraitBase"), Index));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const FString ClassPath = Trait->GetClass()->GetPathName();
|
|
236
|
+
TraitClassNames.Add(MakeShared<FJsonValueString>(ClassPath));
|
|
237
|
+
auto Record = MakeShared<FJsonObject>();
|
|
238
|
+
Record->SetNumberField(TEXT("index"), Index);
|
|
239
|
+
Record->SetStringField(TEXT("classPath"), ClassPath);
|
|
240
|
+
Record->SetStringField(TEXT("className"), Trait->GetClass()->GetName());
|
|
241
|
+
|
|
242
|
+
auto Properties = MakeShared<FJsonObject>();
|
|
243
|
+
for (TFieldIterator<FProperty> PropertyIt(Trait->GetClass()); PropertyIt; ++PropertyIt)
|
|
244
|
+
{
|
|
245
|
+
const FProperty* Property = *PropertyIt;
|
|
246
|
+
if (!Property || Property->HasAnyPropertyFlags(CPF_Transient)) continue;
|
|
247
|
+
Properties->SetStringField(Property->GetName(), ExportTraitProperty(Property, Trait));
|
|
248
|
+
}
|
|
249
|
+
Record->SetObjectField(TEXT("properties"), Properties);
|
|
250
|
+
TraitRecords.Add(MakeShared<FJsonValueObject>(Record));
|
|
251
|
+
}
|
|
252
|
+
Result->SetArrayField(TEXT("traitClasses"), TraitClassNames);
|
|
253
|
+
Result->SetArrayField(TEXT("traits"), TraitRecords);
|
|
254
|
+
Result->SetNumberField(TEXT("traitCount"), TraitClasses.Num());
|
|
255
|
+
return MCPResult(Result);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
TSharedPtr<FJsonValue> FMassHandlers::EnsureEntityConfig(const TSharedPtr<FJsonObject>& Params)
|
|
259
|
+
{
|
|
260
|
+
FString Name;
|
|
261
|
+
FString PackagePath;
|
|
262
|
+
if (!SplitAssetPath(Params, Name, PackagePath))
|
|
263
|
+
{
|
|
264
|
+
return MCPError(TEXT("Provide assetPath (/Game/Folder/Name[.Name]) or name plus packagePath"));
|
|
265
|
+
}
|
|
266
|
+
if (!FPackageName::IsValidLongPackageName(PackagePath))
|
|
267
|
+
{
|
|
268
|
+
return MCPError(FString::Printf(TEXT("Invalid package path: %s"), *PackagePath));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
UClass* ConfigClass = ResolveClass(ConfigClassPath);
|
|
272
|
+
UClass* TraitBaseClass = ResolveClass(TraitBaseClassPath);
|
|
273
|
+
if (!ConfigClass || !TraitBaseClass)
|
|
274
|
+
{
|
|
275
|
+
return MCPError(TEXT("MassSpawner is unavailable; enable MassGameplay/MassSpawner before using ensure_mass_entity_config"));
|
|
276
|
+
}
|
|
277
|
+
if (!ConfigClass->IsChildOf(UDataAsset::StaticClass()) || !TraitBaseClass->IsChildOf(UObject::StaticClass()))
|
|
278
|
+
{
|
|
279
|
+
return MCPError(TEXT("MassSpawner classes are incompatible with this engine version"));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const FString OnConflict = OptionalString(Params, TEXT("onConflict"), TEXT("update")).ToLower();
|
|
283
|
+
if (OnConflict != TEXT("skip") && OnConflict != TEXT("error") && OnConflict != TEXT("update"))
|
|
284
|
+
{
|
|
285
|
+
return MCPError(TEXT("onConflict must be one of: skip, error, update"));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const TSharedPtr<FJsonValue>* TraitsValue = Params->Values.Find(TEXT("traits"));
|
|
289
|
+
const TArray<TSharedPtr<FJsonValue>>* TraitArray = nullptr;
|
|
290
|
+
if (!TraitsValue || !(*TraitsValue).IsValid() || !(*TraitsValue)->TryGetArray(TraitArray) || !TraitArray)
|
|
291
|
+
{
|
|
292
|
+
return MCPError(TEXT("Missing required 'traits' array"));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
struct FRequestedTrait { UClass* Class = nullptr; const TSharedPtr<FJsonObject>* Properties = nullptr; };
|
|
296
|
+
TArray<FRequestedTrait> Requested;
|
|
297
|
+
TSet<UClass*> SeenClasses;
|
|
298
|
+
for (int32 Index = 0; Index < TraitArray->Num(); ++Index)
|
|
299
|
+
{
|
|
300
|
+
const TSharedPtr<FJsonObject>* TraitObject = nullptr;
|
|
301
|
+
if (!(*TraitArray)[Index].IsValid() || !(*TraitArray)[Index]->TryGetObject(TraitObject) || !TraitObject || !(*TraitObject).IsValid())
|
|
302
|
+
{
|
|
303
|
+
return MCPError(FString::Printf(TEXT("traits[%d] must be an object"), Index));
|
|
304
|
+
}
|
|
305
|
+
FString ClassPath;
|
|
306
|
+
if (!(*TraitObject)->TryGetStringField(TEXT("class"), ClassPath) && !(*TraitObject)->TryGetStringField(TEXT("traitClass"), ClassPath))
|
|
307
|
+
{
|
|
308
|
+
return MCPError(FString::Printf(TEXT("traits[%d] requires class or traitClass"), Index));
|
|
309
|
+
}
|
|
310
|
+
UClass* TraitClass = ResolveClass(ClassPath);
|
|
311
|
+
if (!TraitClass || !TraitClass->IsChildOf(TraitBaseClass) || TraitClass->HasAnyClassFlags(CLASS_Abstract))
|
|
312
|
+
{
|
|
313
|
+
return MCPError(FString::Printf(TEXT("traits[%d] is not a concrete UMassEntityTraitBase: %s"), Index, *ClassPath));
|
|
314
|
+
}
|
|
315
|
+
if (SeenClasses.Contains(TraitClass))
|
|
316
|
+
{
|
|
317
|
+
return MCPError(FString::Printf(TEXT("traits[%d] duplicates trait class %s; ordered traits must be unique"), Index, *ClassPath));
|
|
318
|
+
}
|
|
319
|
+
SeenClasses.Add(TraitClass);
|
|
320
|
+
FRequestedTrait& Request = Requested.AddDefaulted_GetRef();
|
|
321
|
+
Request.Class = TraitClass;
|
|
322
|
+
(*TraitObject)->TryGetObjectField(TEXT("properties"), Request.Properties);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Validate every property against a transient trait before loading or
|
|
326
|
+
// creating the real config. This is deliberately done for the whole list,
|
|
327
|
+
// not while appending traits, so a bad property late in the request cannot
|
|
328
|
+
// leave a partially authored asset behind.
|
|
329
|
+
TArray<UObject*> Previews;
|
|
330
|
+
Previews.Reserve(Requested.Num());
|
|
331
|
+
for (int32 Index = 0; Index < Requested.Num(); ++Index)
|
|
332
|
+
{
|
|
333
|
+
const FName PreviewName = MakeUniqueObjectName(GetTransientPackage(), Requested[Index].Class, Requested[Index].Class->GetFName());
|
|
334
|
+
UObject* Preview = NewObject<UObject>(GetTransientPackage(), Requested[Index].Class, PreviewName, RF_Transient);
|
|
335
|
+
if (!Preview)
|
|
336
|
+
{
|
|
337
|
+
ReleasePreviews(Previews);
|
|
338
|
+
return MCPError(FString::Printf(TEXT("Failed to construct transient preview for trait %d (%s)"), Index, *Requested[Index].Class->GetPathName()));
|
|
339
|
+
}
|
|
340
|
+
Preview->AddToRoot();
|
|
341
|
+
Previews.Add(Preview);
|
|
342
|
+
int32 IgnoredProperties = 0;
|
|
343
|
+
if (TSharedPtr<FJsonValue> Error = ValidateAndApplyProperties(Preview, Requested[Index].Properties ? *Requested[Index].Properties : nullptr, Index, IgnoredProperties))
|
|
344
|
+
{
|
|
345
|
+
ReleasePreviews(Previews);
|
|
346
|
+
return Error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
ReleasePreviews(Previews);
|
|
350
|
+
|
|
351
|
+
UObject* ExistingAsset = LoadConfigAsset(Name, PackagePath);
|
|
352
|
+
if (ExistingAsset && !ExistingAsset->IsA(ConfigClass))
|
|
353
|
+
{
|
|
354
|
+
return MCPError(FString::Printf(TEXT("Asset exists but is not a MassEntityConfigAsset: %s"), *ExistingAsset->GetPathName()));
|
|
355
|
+
}
|
|
356
|
+
if (ExistingAsset && OnConflict == TEXT("error"))
|
|
357
|
+
{
|
|
358
|
+
return MCPError(FString::Printf(TEXT("MassEntityConfigAsset '%s' already exists"), *ExistingAsset->GetPathName()));
|
|
359
|
+
}
|
|
360
|
+
if (ExistingAsset && OnConflict == TEXT("skip"))
|
|
361
|
+
{
|
|
362
|
+
TArray<UClass*> ExistingClasses; FString Error;
|
|
363
|
+
if (!ReadTraitClasses(ExistingAsset, ExistingClasses, Error)) return MCPError(Error);
|
|
364
|
+
return MakeExistingResult(ExistingAsset, ExistingClasses, false, 0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
bool bCreated = false;
|
|
368
|
+
if (!ExistingAsset)
|
|
369
|
+
{
|
|
370
|
+
auto Created = MCPCreateAssetIdempotent<UObject>(Name, PackagePath, TEXT("error"), TEXT("MassEntityConfigAsset"), ConfigClass, nullptr);
|
|
371
|
+
if (Created.EarlyReturn) return Created.EarlyReturn;
|
|
372
|
+
ExistingAsset = Created.Asset;
|
|
373
|
+
bCreated = true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
TArray<UClass*> ExistingClasses;
|
|
377
|
+
FString ReadError;
|
|
378
|
+
if (!ReadTraitClasses(ExistingAsset, ExistingClasses, ReadError)) return MCPError(ReadError);
|
|
379
|
+
if (ExistingClasses.Num() > Requested.Num())
|
|
380
|
+
{
|
|
381
|
+
return MCPError(TEXT("Existing config has more traits than requested; refusing destructive replacement"));
|
|
382
|
+
}
|
|
383
|
+
for (int32 Index = 0; Index < ExistingClasses.Num(); ++Index)
|
|
384
|
+
{
|
|
385
|
+
if (ExistingClasses[Index] != Requested[Index].Class)
|
|
386
|
+
{
|
|
387
|
+
return MCPError(FString::Printf(TEXT("Existing trait order conflicts at index %d; refusing destructive replacement"), Index));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// For an update, duplicate the existing instanced traits into the transient
|
|
392
|
+
// package as a second preflight. This catches property paths that are valid
|
|
393
|
+
// on the class but invalid for the currently-authored trait instance before
|
|
394
|
+
// Modify() or any real property write is issued.
|
|
395
|
+
if (!bCreated)
|
|
396
|
+
{
|
|
397
|
+
Previews.Reset();
|
|
398
|
+
for (int32 Index = 0; Index < Requested.Num(); ++Index)
|
|
399
|
+
{
|
|
400
|
+
UObject* SourceTrait = Index < ExistingClasses.Num() ? GetTraitAtIndex(ExistingAsset, Index) : nullptr;
|
|
401
|
+
UObject* Preview = SourceTrait
|
|
402
|
+
? StaticDuplicateObject(SourceTrait, GetTransientPackage(), NAME_None, RF_Transient)
|
|
403
|
+
: NewObject<UObject>(GetTransientPackage(), Requested[Index].Class,
|
|
404
|
+
MakeUniqueObjectName(GetTransientPackage(), Requested[Index].Class, Requested[Index].Class->GetFName()), RF_Transient);
|
|
405
|
+
if (!Preview)
|
|
406
|
+
{
|
|
407
|
+
ReleasePreviews(Previews);
|
|
408
|
+
return MCPError(FString::Printf(TEXT("Failed to construct transient update preview for trait %d"), Index));
|
|
409
|
+
}
|
|
410
|
+
Preview->AddToRoot();
|
|
411
|
+
Previews.Add(Preview);
|
|
412
|
+
int32 IgnoredProperties = 0;
|
|
413
|
+
if (TSharedPtr<FJsonValue> Error = ValidateAndApplyProperties(Preview, Requested[Index].Properties ? *Requested[Index].Properties : nullptr, Index, IgnoredProperties))
|
|
414
|
+
{
|
|
415
|
+
ReleasePreviews(Previews);
|
|
416
|
+
return Error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
ReleasePreviews(Previews);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// AddTrait appends to the live Traits array and constructs an
|
|
423
|
+
// RF_Transactional subobject, so a failure partway through the loop used to
|
|
424
|
+
// leave traits 0..N-1 on the asset and return an error. Modify() alone did
|
|
425
|
+
// not help: with no FScopedTransaction open there is no transaction to
|
|
426
|
+
// record into, so undo could not recover it either, despite the comment
|
|
427
|
+
// that used to sit here claiming otherwise.
|
|
428
|
+
//
|
|
429
|
+
// Cancelling the transaction on any failure is what makes this all or
|
|
430
|
+
// nothing. The preflights above validate against a duplicated instance, so
|
|
431
|
+
// a property that only fails on the real object still reaches this loop.
|
|
432
|
+
const bool bShouldActuallyTransact = GEditor != nullptr;
|
|
433
|
+
FScopedTransaction Transaction(
|
|
434
|
+
NSLOCTEXT("UEMCP", "EnsureMassEntityConfig", "Author Mass Entity Config Traits"),
|
|
435
|
+
bShouldActuallyTransact);
|
|
436
|
+
ExistingAsset->Modify();
|
|
437
|
+
int32 PropertiesSet = 0;
|
|
438
|
+
for (int32 Index = 0; Index < Requested.Num(); ++Index)
|
|
439
|
+
{
|
|
440
|
+
UObject* Trait = nullptr;
|
|
441
|
+
if (Index < ExistingClasses.Num()) Trait = GetTraitAtIndex(ExistingAsset, Index);
|
|
442
|
+
else if (!AddTrait(ExistingAsset, Requested[Index].Class, Trait, ReadError))
|
|
443
|
+
{
|
|
444
|
+
Transaction.Cancel();
|
|
445
|
+
return MCPError(ReadError);
|
|
446
|
+
}
|
|
447
|
+
if (TSharedPtr<FJsonValue> Error = ValidateAndApplyProperties(Trait, Requested[Index].Properties ? *Requested[Index].Properties : nullptr, Index, PropertiesSet))
|
|
448
|
+
{
|
|
449
|
+
Transaction.Cancel();
|
|
450
|
+
return Error;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
ExistingAsset->MarkPackageDirty();
|
|
455
|
+
if (!SaveAssetPackage(ExistingAsset))
|
|
456
|
+
{
|
|
457
|
+
return MCPError(FString::Printf(TEXT("Failed to save MassEntityConfigAsset: %s"), *ExistingAsset->GetPathName()));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
TSharedPtr<FJsonObject> Result = MCPSuccess();
|
|
461
|
+
if (bCreated) MCPSetCreated(Result); else { MCPSetExisted(Result); MCPSetUpdated(Result); }
|
|
462
|
+
Result->SetStringField(TEXT("assetPath"), ExistingAsset->GetPathName());
|
|
463
|
+
Result->SetNumberField(TEXT("traits"), Requested.Num());
|
|
464
|
+
Result->SetNumberField(TEXT("propertiesSet"), PropertiesSet);
|
|
465
|
+
if (bCreated) MCPSetDeleteAssetRollback(Result, ExistingAsset->GetPathName());
|
|
466
|
+
return MCPResult(Result);
|
|
467
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CoreMinimal.h"
|
|
4
|
+
#include "Dom/JsonValue.h"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Editor-only authoring helpers for Mass entity configuration assets.
|
|
8
|
+
*
|
|
9
|
+
* The implementation intentionally resolves Mass classes by reflection. This
|
|
10
|
+
* keeps the bridge loadable on engine versions/projects where Mass is not
|
|
11
|
+
* enabled while still providing a strongly validated path when MassSpawner is
|
|
12
|
+
* present.
|
|
13
|
+
*/
|
|
14
|
+
class FMassHandlers
|
|
15
|
+
{
|
|
16
|
+
public:
|
|
17
|
+
static void RegisterHandlers(class FMCPHandlerRegistry& Registry);
|
|
18
|
+
|
|
19
|
+
private:
|
|
20
|
+
static TSharedPtr<FJsonValue> EnsureEntityConfig(const TSharedPtr<FJsonObject>& Params);
|
|
21
|
+
static TSharedPtr<FJsonValue> ReadEntityConfig(const TSharedPtr<FJsonObject>& Params);
|
|
22
|
+
};
|