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
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
#include "Dom/JsonObject.h"
|
|
6
6
|
#include "Serialization/JsonReader.h"
|
|
7
7
|
#include "Serialization/JsonSerializer.h"
|
|
8
|
+
#include "Serialization/JsonWriter.h"
|
|
9
|
+
#include "Policies/CondensedJsonPrintPolicy.h"
|
|
8
10
|
#include "UObject/UnrealType.h"
|
|
9
11
|
#include "UObject/PropertyPortFlags.h"
|
|
10
12
|
#include "UObject/SoftObjectPtr.h"
|
|
13
|
+
#include "StructUtils/InstancedStruct.h"
|
|
11
14
|
#include "GameplayTagContainer.h"
|
|
12
15
|
#include "GameplayTagsManager.h"
|
|
13
16
|
#include "Engine/Blueprint.h"
|
|
@@ -23,6 +26,87 @@ namespace MCPJsonProperty
|
|
|
23
26
|
{
|
|
24
27
|
inline bool SetJsonOnProperty(FProperty* Prop, void* ValueAddr, const TSharedPtr<FJsonValue>& InValue, FString& OutError);
|
|
25
28
|
|
|
29
|
+
// The reference-property kinds, as one closed answer per property.
|
|
30
|
+
//
|
|
31
|
+
// The reflection types nest: FClassProperty derives from FObjectProperty,
|
|
32
|
+
// and FSoftClassProperty from FSoftObjectProperty; FWeakObjectProperty and
|
|
33
|
+
// FLazyObjectProperty are siblings of both under FObjectPropertyBase.
|
|
34
|
+
// CastField<Base>() succeeds on a derived instance, so a chain that tests
|
|
35
|
+
// the base first swallows the derived kind and leaves the derived branch
|
|
36
|
+
// unreachable. That is exactly how a TSubclassOf<> field came to be
|
|
37
|
+
// resolved by the generic asset loader instead of the class loader: the
|
|
38
|
+
// class branch sat below the object branch and never ran (#935).
|
|
39
|
+
//
|
|
40
|
+
// Classifying once and switching on the answer is used here in preference
|
|
41
|
+
// to simply reordering the branches. Ordering is invisible at the point of
|
|
42
|
+
// use, is not checkable by a test, and one tidy-up edit that moves a block
|
|
43
|
+
// silently reinstates the bug. A wrong answer here is a single function to
|
|
44
|
+
// read and a single function to test, and no later edit to the call sites
|
|
45
|
+
// can bring the dead branch back.
|
|
46
|
+
enum class ERefKind : uint8
|
|
47
|
+
{
|
|
48
|
+
NotAReference,
|
|
49
|
+
SoftClass,
|
|
50
|
+
SoftObject,
|
|
51
|
+
Class,
|
|
52
|
+
Object,
|
|
53
|
+
WeakObject,
|
|
54
|
+
LazyObject,
|
|
55
|
+
Interface,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
inline ERefKind ClassifyReference(const FProperty* Prop)
|
|
59
|
+
{
|
|
60
|
+
if (!Prop) return ERefKind::NotAReference;
|
|
61
|
+
// Most derived first: each test is reached only once every
|
|
62
|
+
// more-derived test above it has failed.
|
|
63
|
+
if (Prop->IsA<FSoftClassProperty>()) return ERefKind::SoftClass;
|
|
64
|
+
if (Prop->IsA<FSoftObjectProperty>()) return ERefKind::SoftObject;
|
|
65
|
+
if (Prop->IsA<FClassProperty>()) return ERefKind::Class;
|
|
66
|
+
if (Prop->IsA<FObjectProperty>()) return ERefKind::Object;
|
|
67
|
+
if (Prop->IsA<FWeakObjectProperty>()) return ERefKind::WeakObject;
|
|
68
|
+
if (Prop->IsA<FLazyObjectProperty>()) return ERefKind::LazyObject;
|
|
69
|
+
if (Prop->IsA<FInterfaceProperty>()) return ERefKind::Interface;
|
|
70
|
+
return ERefKind::NotAReference;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Resolve a class path the way a caller means it, without inventing a
|
|
74
|
+
// suffix the path did not ask for.
|
|
75
|
+
//
|
|
76
|
+
// #489: a caller naming a Blueprint commonly passes the asset path
|
|
77
|
+
// (/Game/Foo/BP_GameMode.BP_GameMode) for a class-typed field, and the
|
|
78
|
+
// generated class is that path plus "_C".
|
|
79
|
+
// #928: the suffix used to be appended unconditionally on the soft-class
|
|
80
|
+
// path, so a native /Script/Module.ClassName reference was stored as
|
|
81
|
+
// /Script/Module.ClassName_C, which names no class at all, and the write
|
|
82
|
+
// still reported success. The suffix is now only ever used when the path
|
|
83
|
+
// as written does not name a class and the suffixed form does.
|
|
84
|
+
inline UClass* ResolveClassPath(const FString& Path)
|
|
85
|
+
{
|
|
86
|
+
if (Path.IsEmpty()) return nullptr;
|
|
87
|
+
|
|
88
|
+
if (UClass* Direct = LoadClass<UObject>(nullptr, *Path))
|
|
89
|
+
{
|
|
90
|
+
return Direct;
|
|
91
|
+
}
|
|
92
|
+
// A native class lives at its /Script/ path and never carries "_C".
|
|
93
|
+
if (!Path.StartsWith(TEXT("/Script/")) && !Path.EndsWith(TEXT("_C")))
|
|
94
|
+
{
|
|
95
|
+
const FString WithSuffix = Path + TEXT("_C");
|
|
96
|
+
if (UClass* Generated = LoadClass<UObject>(nullptr, *WithSuffix))
|
|
97
|
+
{
|
|
98
|
+
return Generated;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Last ditch: load the asset as a UBlueprint and take its generated
|
|
102
|
+
// class. Covers paths written without the .Name object suffix.
|
|
103
|
+
if (UBlueprint* BP = Cast<UBlueprint>(StaticLoadObject(UBlueprint::StaticClass(), nullptr, *Path)))
|
|
104
|
+
{
|
|
105
|
+
return BP->GeneratedClass;
|
|
106
|
+
}
|
|
107
|
+
return nullptr;
|
|
108
|
+
}
|
|
109
|
+
|
|
26
110
|
// One (key, value) write for a TMap, in whichever JSON shape the caller
|
|
27
111
|
// used. `KeyJson` is null for the object shape, where the field name is the
|
|
28
112
|
// key text.
|
|
@@ -117,32 +201,33 @@ namespace MCPJsonProperty
|
|
|
117
201
|
// to ImportText and surfaced "asset not found: None".
|
|
118
202
|
if (Value->Type == EJson::Null)
|
|
119
203
|
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
204
|
+
// One dispatch on the classified kind: FClassProperty used to sit
|
|
205
|
+
// below FObjectProperty here and never be reached (#935). Clearing
|
|
206
|
+
// happens to mean the same thing for both, so this branch was not
|
|
207
|
+
// the one that corrupted data, but leaving the same inverted chain
|
|
208
|
+
// in place invites the next reader to copy it.
|
|
209
|
+
switch (ClassifyReference(Prop))
|
|
126
210
|
{
|
|
127
|
-
|
|
211
|
+
case ERefKind::SoftClass:
|
|
212
|
+
case ERefKind::SoftObject:
|
|
213
|
+
CastFieldChecked<FSoftObjectProperty>(Prop)->SetPropertyValue(ValueAddr, FSoftObjectPtr());
|
|
128
214
|
return true;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
WOP->SetObjectPropertyValue(ValueAddr, nullptr);
|
|
215
|
+
case ERefKind::Class:
|
|
216
|
+
case ERefKind::Object:
|
|
217
|
+
CastFieldChecked<FObjectProperty>(Prop)->SetObjectPropertyValue(ValueAddr, nullptr);
|
|
133
218
|
return true;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
CP->SetObjectPropertyValue(ValueAddr, nullptr);
|
|
219
|
+
case ERefKind::WeakObject:
|
|
220
|
+
CastFieldChecked<FWeakObjectProperty>(Prop)->SetObjectPropertyValue(ValueAddr, nullptr);
|
|
138
221
|
return true;
|
|
139
|
-
|
|
140
|
-
if (FInterfaceProperty* IP = CastField<FInterfaceProperty>(Prop))
|
|
222
|
+
case ERefKind::Interface:
|
|
141
223
|
{
|
|
142
224
|
FScriptInterface Empty;
|
|
143
|
-
|
|
225
|
+
CastFieldChecked<FInterfaceProperty>(Prop)->SetPropertyValue(ValueAddr, Empty);
|
|
144
226
|
return true;
|
|
145
227
|
}
|
|
228
|
+
default:
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
146
231
|
OutError = FString::Printf(TEXT("property '%s' is not an object/class/interface reference; null value not allowed"), *Prop->GetName());
|
|
147
232
|
return false;
|
|
148
233
|
}
|
|
@@ -351,84 +436,81 @@ namespace MCPJsonProperty
|
|
|
351
436
|
}
|
|
352
437
|
}
|
|
353
438
|
|
|
354
|
-
//
|
|
355
|
-
|
|
439
|
+
// Object, class and soft references, all addressed by path. One
|
|
440
|
+
// dispatch on the classified kind (see ClassifyReference), so the
|
|
441
|
+
// class kinds are handled by the class-aware code rather than being
|
|
442
|
+
// swallowed by their base's branch (#935). A value that is not a
|
|
443
|
+
// string falls out of the switch to the ImportText fallback below,
|
|
444
|
+
// exactly as it did when these were separate `if` blocks.
|
|
445
|
+
switch (ClassifyReference(Prop))
|
|
446
|
+
{
|
|
447
|
+
case ERefKind::Class:
|
|
356
448
|
{
|
|
357
449
|
FString Path;
|
|
358
450
|
if (Value->TryGetString(Path) && !Path.IsEmpty())
|
|
359
451
|
{
|
|
360
|
-
|
|
361
|
-
if (!Loaded) { OutError = FString::Printf(TEXT("
|
|
362
|
-
|
|
452
|
+
UClass* Loaded = ResolveClassPath(Path);
|
|
453
|
+
if (!Loaded) { OutError = FString::Printf(TEXT("class not found: %s"), *Path); return false; }
|
|
454
|
+
CastFieldChecked<FClassProperty>(Prop)->SetObjectPropertyValue(ValueAddr, Loaded);
|
|
363
455
|
return true;
|
|
364
456
|
}
|
|
457
|
+
break;
|
|
365
458
|
}
|
|
366
|
-
|
|
367
|
-
// Hard UClass ref - accept class path
|
|
368
|
-
if (FClassProperty* ClassProp = CastField<FClassProperty>(Prop))
|
|
459
|
+
case ERefKind::Object:
|
|
369
460
|
{
|
|
370
461
|
FString Path;
|
|
371
462
|
if (Value->TryGetString(Path) && !Path.IsEmpty())
|
|
372
463
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
{
|
|
376
|
-
|
|
377
|
-
// (/Game/Foo/BP_GameMode.BP_GameMode) for a TSubclassOf
|
|
378
|
-
// field. Retry with the generated-class suffix.
|
|
379
|
-
if (!Path.EndsWith(TEXT("_C")))
|
|
380
|
-
{
|
|
381
|
-
const FString WithSuffix = Path + TEXT("_C");
|
|
382
|
-
Loaded = LoadClass<UObject>(nullptr, *WithSuffix);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
if (!Loaded)
|
|
386
|
-
{
|
|
387
|
-
// Last-ditch: load the asset as UBlueprint and grab its
|
|
388
|
-
// GeneratedClass. Covers paths that omit the .Name suffix.
|
|
389
|
-
if (UBlueprint* BP = Cast<UBlueprint>(StaticLoadObject(UBlueprint::StaticClass(), nullptr, *Path)))
|
|
390
|
-
{
|
|
391
|
-
Loaded = BP->GeneratedClass;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (!Loaded) { OutError = FString::Printf(TEXT("class not found: %s"), *Path); return false; }
|
|
395
|
-
ClassProp->SetObjectPropertyValue(ValueAddr, Loaded);
|
|
464
|
+
FObjectProperty* ObjProp = CastFieldChecked<FObjectProperty>(Prop);
|
|
465
|
+
UObject* Loaded = StaticLoadObject(ObjProp->PropertyClass, nullptr, *Path);
|
|
466
|
+
if (!Loaded) { OutError = FString::Printf(TEXT("asset not found: %s"), *Path); return false; }
|
|
467
|
+
ObjProp->SetObjectPropertyValue(ValueAddr, Loaded);
|
|
396
468
|
return true;
|
|
397
469
|
}
|
|
470
|
+
break;
|
|
398
471
|
}
|
|
399
|
-
|
|
400
|
-
// Soft class ref - accept class path string, same Blueprint suffix tolerance.
|
|
401
|
-
if (FSoftClassProperty* SoftClassProp = CastField<FSoftClassProperty>(Prop))
|
|
472
|
+
case ERefKind::SoftClass:
|
|
402
473
|
{
|
|
403
474
|
FString Path;
|
|
404
475
|
if (Value->TryGetString(Path))
|
|
405
476
|
{
|
|
477
|
+
FSoftClassProperty* SoftClassProp = CastFieldChecked<FSoftClassProperty>(Prop);
|
|
406
478
|
if (Path.IsEmpty())
|
|
407
479
|
{
|
|
408
480
|
SoftClassProp->SetPropertyValue(ValueAddr, FSoftObjectPtr());
|
|
409
481
|
return true;
|
|
410
482
|
}
|
|
411
|
-
|
|
483
|
+
// #928: store the path that actually names a class. Only a
|
|
484
|
+
// Blueprint-generated class carries the "_C" suffix, and it is
|
|
485
|
+
// added only when the path as written names nothing and the
|
|
486
|
+
// suffixed form does. A path that resolves to neither is
|
|
487
|
+
// stored verbatim: a soft reference to an asset that does not
|
|
488
|
+
// exist yet is legitimate, and silently rewriting it was how a
|
|
489
|
+
// native /Script/ reference got corrupted.
|
|
490
|
+
if (UClass* Resolved = ResolveClassPath(Path))
|
|
412
491
|
{
|
|
413
|
-
|
|
492
|
+
SoftClassProp->SetPropertyValue(ValueAddr, FSoftObjectPtr(FSoftObjectPath(Resolved)));
|
|
493
|
+
return true;
|
|
414
494
|
}
|
|
415
|
-
|
|
416
|
-
SoftClassProp->SetPropertyValue(ValueAddr, FSoftObjectPtr(PathObj));
|
|
495
|
+
SoftClassProp->SetPropertyValue(ValueAddr, FSoftObjectPtr(FSoftObjectPath(Path)));
|
|
417
496
|
return true;
|
|
418
497
|
}
|
|
498
|
+
break;
|
|
419
499
|
}
|
|
420
|
-
|
|
421
|
-
// Soft object ref - accept path string
|
|
422
|
-
if (FSoftObjectProperty* SoftObjProp = CastField<FSoftObjectProperty>(Prop))
|
|
500
|
+
case ERefKind::SoftObject:
|
|
423
501
|
{
|
|
424
502
|
FString Path;
|
|
425
503
|
if (Value->TryGetString(Path))
|
|
426
504
|
{
|
|
427
505
|
FSoftObjectPath PathObj(Path);
|
|
428
506
|
FSoftObjectPtr Ptr(PathObj);
|
|
429
|
-
|
|
507
|
+
CastFieldChecked<FSoftObjectProperty>(Prop)->SetPropertyValue(ValueAddr, Ptr);
|
|
430
508
|
return true;
|
|
431
509
|
}
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
default:
|
|
513
|
+
break;
|
|
432
514
|
}
|
|
433
515
|
|
|
434
516
|
// Enum / Byte-with-enum: accept friendly aliases ("center", "Center"),
|
|
@@ -552,6 +634,208 @@ namespace MCPJsonProperty
|
|
|
552
634
|
return bOk;
|
|
553
635
|
}
|
|
554
636
|
|
|
637
|
+
// ── Readback verification ────────────────────────────────────────────────
|
|
638
|
+
//
|
|
639
|
+
// #935: a write that did not take must not come back as a success. The
|
|
640
|
+
// setter returns true the moment an assignment executes, and nothing used
|
|
641
|
+
// to look at what was actually stored afterwards, so a reference that
|
|
642
|
+
// resolved to the wrong thing, a container that quietly emptied, or a
|
|
643
|
+
// value that never reached the row the caller named all reported
|
|
644
|
+
// success: true with the broken asset saved to disk.
|
|
645
|
+
|
|
646
|
+
/** A compact, readable rendering of a JSON value for an error message. */
|
|
647
|
+
inline FString DescribeJsonValue(const TSharedPtr<FJsonValue>& Value)
|
|
648
|
+
{
|
|
649
|
+
if (!Value.IsValid()) return TEXT("(none)");
|
|
650
|
+
switch (Value->Type)
|
|
651
|
+
{
|
|
652
|
+
case EJson::Null: return TEXT("null");
|
|
653
|
+
case EJson::Boolean: return Value->AsBool() ? TEXT("true") : TEXT("false");
|
|
654
|
+
case EJson::Number: return FString::SanitizeFloat(Value->AsNumber());
|
|
655
|
+
case EJson::String: return Value->AsString();
|
|
656
|
+
default:
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
FString Out;
|
|
660
|
+
using FCondensedWriter = TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>;
|
|
661
|
+
const TSharedRef<FCondensedWriter> Writer =
|
|
662
|
+
TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>::Create(&Out);
|
|
663
|
+
if (Value->Type == EJson::Object)
|
|
664
|
+
{
|
|
665
|
+
FJsonSerializer::Serialize(Value->AsObject().ToSharedRef(), Writer);
|
|
666
|
+
}
|
|
667
|
+
else
|
|
668
|
+
{
|
|
669
|
+
FJsonSerializer::Serialize(Value->AsArray(), Writer);
|
|
670
|
+
}
|
|
671
|
+
return Out;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/** True when two independent applications of one request must produce
|
|
675
|
+
* values that compare Identical.
|
|
676
|
+
*
|
|
677
|
+
* Two kinds of value are not in that class, and both would report a
|
|
678
|
+
* mismatch that is not one:
|
|
679
|
+
*
|
|
680
|
+
* FText identity is per instance. Two imports of the same source string
|
|
681
|
+
* carry different keys and are never Identical.
|
|
682
|
+
*
|
|
683
|
+
* An instanced reference is a fresh subobject on every import, so the two
|
|
684
|
+
* applications point at two different objects by design.
|
|
685
|
+
*
|
|
686
|
+
* Anything unrecognised, and anything past the depth bound, answers
|
|
687
|
+
* "no", so the conservative path is the one that skips the comparison
|
|
688
|
+
* rather than the one that fails a write that was fine. */
|
|
689
|
+
inline bool PropertyIsComparableByIdentity(const FProperty* Prop, int32 Depth = 0)
|
|
690
|
+
{
|
|
691
|
+
if (!Prop || Depth > 12) return false;
|
|
692
|
+
if (Prop->HasAnyPropertyFlags(CPF_InstancedReference | CPF_ContainsInstancedReference)) return false;
|
|
693
|
+
if (Prop->IsA<FTextProperty>()) return false;
|
|
694
|
+
|
|
695
|
+
if (const FArrayProperty* ArrProp = CastField<FArrayProperty>(Prop))
|
|
696
|
+
{
|
|
697
|
+
return PropertyIsComparableByIdentity(ArrProp->Inner, Depth + 1);
|
|
698
|
+
}
|
|
699
|
+
if (const FSetProperty* SetProp = CastField<FSetProperty>(Prop))
|
|
700
|
+
{
|
|
701
|
+
return PropertyIsComparableByIdentity(SetProp->ElementProp, Depth + 1);
|
|
702
|
+
}
|
|
703
|
+
if (const FMapProperty* MapProp = CastField<FMapProperty>(Prop))
|
|
704
|
+
{
|
|
705
|
+
return PropertyIsComparableByIdentity(MapProp->KeyProp, Depth + 1)
|
|
706
|
+
&& PropertyIsComparableByIdentity(MapProp->ValueProp, Depth + 1);
|
|
707
|
+
}
|
|
708
|
+
if (const FStructProperty* StructProp = CastField<FStructProperty>(Prop))
|
|
709
|
+
{
|
|
710
|
+
for (TFieldIterator<FProperty> It(StructProp->Struct); It; ++It)
|
|
711
|
+
{
|
|
712
|
+
if (!PropertyIsComparableByIdentity(*It, Depth + 1)) return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return true;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** The comparable form of an asset path.
|
|
719
|
+
*
|
|
720
|
+
* Two normalisations, and only the two the resolver is allowed to apply:
|
|
721
|
+
* the "Pkg.Obj" long form of "Pkg" that the content browser writes as
|
|
722
|
+
* "Pkg", and the Blueprint generated class "Pkg.Obj_C" of that same
|
|
723
|
+
* asset. Both are recognised by the object name repeating the package
|
|
724
|
+
* leaf, which is what makes them the same asset; a "_C" that does not
|
|
725
|
+
* produce that repetition is part of the name and is left alone, so
|
|
726
|
+
* /Script/Engine.Character and /Script/Engine.Character_C stay different
|
|
727
|
+
* paths, and an asset genuinely called Foo_C still compares equal to
|
|
728
|
+
* itself. A subobject path is compared whole. */
|
|
729
|
+
inline FString NormalizeAssetPathForCompare(const FString& InPath)
|
|
730
|
+
{
|
|
731
|
+
FString Path = InPath.TrimStartAndEnd();
|
|
732
|
+
if (Path.IsEmpty() || Path.Contains(TEXT(":"))) return Path;
|
|
733
|
+
|
|
734
|
+
int32 DotPos = INDEX_NONE;
|
|
735
|
+
if (!Path.FindLastChar(TEXT('.'), DotPos)) return Path;
|
|
736
|
+
|
|
737
|
+
const FString Package = Path.Left(DotPos);
|
|
738
|
+
const FString ObjectName = Path.Mid(DotPos + 1);
|
|
739
|
+
|
|
740
|
+
int32 SlashPos = INDEX_NONE;
|
|
741
|
+
const FString LeafName = Package.FindLastChar(TEXT('/'), SlashPos)
|
|
742
|
+
? Package.Mid(SlashPos + 1)
|
|
743
|
+
: Package;
|
|
744
|
+
|
|
745
|
+
if (LeafName.Equals(ObjectName, ESearchCase::IgnoreCase)) return Package;
|
|
746
|
+
if (ObjectName.EndsWith(TEXT("_C")) &&
|
|
747
|
+
LeafName.Equals(ObjectName.LeftChop(2), ESearchCase::IgnoreCase))
|
|
748
|
+
{
|
|
749
|
+
return Package;
|
|
750
|
+
}
|
|
751
|
+
return Path;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/** Read the stored value back and answer whether it is what was asked for.
|
|
755
|
+
*
|
|
756
|
+
* Three checks, because they fail on different things.
|
|
757
|
+
*
|
|
758
|
+
* A soft reference is never resolved at write time, so its stored text is
|
|
759
|
+
* whatever the setter chose to write. Comparing that text against the
|
|
760
|
+
* requested path is the only thing that catches a path the setter
|
|
761
|
+
* rewrote, which is exactly how a native /Script/ class reference used to
|
|
762
|
+
* end up with a "_C" suffix appended (#928).
|
|
763
|
+
*
|
|
764
|
+
* A hard reference that was asked for by path must not have come out
|
|
765
|
+
* null. Comparing its path is not useful, because a redirector legally
|
|
766
|
+
* answers a different path than the one requested.
|
|
767
|
+
*
|
|
768
|
+
* Everything else is compared by identity against an independent buffer
|
|
769
|
+
* the same request was applied to. That is what catches a value that
|
|
770
|
+
* never reached the destination: a container that stored fewer entries
|
|
771
|
+
* than it was handed, a row copy that dropped the field, a reference
|
|
772
|
+
* cleared by a later step. Values whose two applications are not
|
|
773
|
+
* required to be identical, text and instanced references, are exempt
|
|
774
|
+
* rather than failed.
|
|
775
|
+
*
|
|
776
|
+
* On a mismatch OutDetail carries the requested and the stored value. */
|
|
777
|
+
inline bool VerifyJsonOnProperty(
|
|
778
|
+
FProperty* Prop,
|
|
779
|
+
const void* ValueAddr,
|
|
780
|
+
const TSharedPtr<FJsonValue>& Requested,
|
|
781
|
+
FString& OutDetail)
|
|
782
|
+
{
|
|
783
|
+
if (!Prop || !ValueAddr || !Requested.IsValid()) return true;
|
|
784
|
+
|
|
785
|
+
FString StoredText;
|
|
786
|
+
Prop->ExportTextItem_Direct(StoredText, ValueAddr, nullptr, nullptr, PPF_None);
|
|
787
|
+
|
|
788
|
+
FString RequestedPath;
|
|
789
|
+
const bool bRequestedPath = Requested->TryGetString(RequestedPath) && !RequestedPath.IsEmpty();
|
|
790
|
+
const ERefKind Kind = ClassifyReference(Prop);
|
|
791
|
+
|
|
792
|
+
if (bRequestedPath && (Kind == ERefKind::SoftClass || Kind == ERefKind::SoftObject))
|
|
793
|
+
{
|
|
794
|
+
const FString Stored = CastFieldChecked<FSoftObjectProperty>(Prop)->GetPropertyValue(ValueAddr).ToString();
|
|
795
|
+
if (!NormalizeAssetPathForCompare(Stored).Equals(
|
|
796
|
+
NormalizeAssetPathForCompare(RequestedPath), ESearchCase::IgnoreCase))
|
|
797
|
+
{
|
|
798
|
+
OutDetail = FString::Printf(TEXT("requested '%s', stored '%s'"), *RequestedPath, *Stored);
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (bRequestedPath && (Kind == ERefKind::Class || Kind == ERefKind::Object))
|
|
805
|
+
{
|
|
806
|
+
if (CastFieldChecked<FObjectProperty>(Prop)->GetObjectPropertyValue(ValueAddr) == nullptr)
|
|
807
|
+
{
|
|
808
|
+
OutDetail = FString::Printf(TEXT("requested '%s', stored nothing"), *RequestedPath);
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (!PropertyIsComparableByIdentity(Prop)) return true;
|
|
815
|
+
|
|
816
|
+
FDefaultConstructedPropertyElement Reference(Prop);
|
|
817
|
+
FString Ignored;
|
|
818
|
+
if (!SetJsonOnProperty(Prop, Reference.GetObjAddress(), Requested, Ignored))
|
|
819
|
+
{
|
|
820
|
+
// The same request applied cleanly a moment ago; if it does not
|
|
821
|
+
// apply now the stored value cannot be trusted either.
|
|
822
|
+
OutDetail = FString::Printf(
|
|
823
|
+
TEXT("requested %s, stored '%s' (the request no longer applies: %s)"),
|
|
824
|
+
*DescribeJsonValue(Requested), *StoredText, *Ignored);
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
if (!Prop->Identical(ValueAddr, Reference.GetObjAddress(), PPF_None))
|
|
828
|
+
{
|
|
829
|
+
FString ReferenceText;
|
|
830
|
+
Prop->ExportTextItem_Direct(ReferenceText, Reference.GetObjAddress(), nullptr, nullptr, PPF_None);
|
|
831
|
+
OutDetail = FString::Printf(
|
|
832
|
+
TEXT("requested %s (which resolves to '%s'), stored '%s'"),
|
|
833
|
+
*DescribeJsonValue(Requested), *ReferenceText, *StoredText);
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
|
|
555
839
|
// Resolve a dotted property path that may index arrays ("Traits[2]") and
|
|
556
840
|
// follow object references (instanced subobjects), e.g.
|
|
557
841
|
// "Config.Traits[1].Params.RepresentationActorManagementClass" on a
|
|
@@ -571,12 +855,13 @@ namespace MCPJsonProperty
|
|
|
571
855
|
if (Parts.Num() == 0) { OutError = TEXT("empty property name"); return false; }
|
|
572
856
|
|
|
573
857
|
void* Container = Root;
|
|
574
|
-
UStruct* ContainerStruct = Root->GetClass();
|
|
858
|
+
const UStruct* ContainerStruct = Root->GetClass();
|
|
575
859
|
UObject* Owner = Root;
|
|
576
860
|
|
|
577
861
|
for (int32 i = 0; i < Parts.Num(); ++i)
|
|
578
862
|
{
|
|
579
863
|
FString Token = Parts[i];
|
|
864
|
+
const FString PathToken = Token;
|
|
580
865
|
int32 Index = INDEX_NONE;
|
|
581
866
|
int32 BracketPos;
|
|
582
867
|
if (Token.FindChar(TEXT('['), BracketPos))
|
|
@@ -595,12 +880,36 @@ namespace MCPJsonProperty
|
|
|
595
880
|
|
|
596
881
|
if (Index != INDEX_NONE)
|
|
597
882
|
{
|
|
598
|
-
FArrayProperty* ArrProp = CastField<FArrayProperty>(Prop)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
883
|
+
if (FArrayProperty* ArrProp = CastField<FArrayProperty>(Prop))
|
|
884
|
+
{
|
|
885
|
+
FScriptArrayHelper H(ArrProp, ValueAddr);
|
|
886
|
+
if (Index < 0 || Index >= H.Num()) { OutError = FString::Printf(TEXT("index %d out of range on '%s' (num=%d)"), Index, *Token, H.Num()); return false; }
|
|
887
|
+
Prop = ArrProp->Inner;
|
|
888
|
+
ValueAddr = H.GetRawPtr(Index);
|
|
889
|
+
}
|
|
890
|
+
// #927: a UPROPERTY declared as a C-style fixed array, e.g.
|
|
891
|
+
// `int32 Foo[3]`, is ONE FProperty with ArrayDim == 3 rather than
|
|
892
|
+
// an FArrayProperty. Without this branch every indexed write to
|
|
893
|
+
// one landed on element 0, or was refused as "not an array",
|
|
894
|
+
// which is how RecastNavMesh's three navmesh generation tiers
|
|
895
|
+
// became unreachable: only the Low tier could be read or written
|
|
896
|
+
// while the engine generated from Default and High.
|
|
897
|
+
else if (Prop->ArrayDim > 1)
|
|
898
|
+
{
|
|
899
|
+
if (Index < 0 || Index >= Prop->ArrayDim)
|
|
900
|
+
{
|
|
901
|
+
OutError = FString::Printf(TEXT("index %d out of range on fixed array '%s' (ArrayDim=%d)"), Index, *Token, Prop->ArrayDim);
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
// Re-address from the container so the element offset is the
|
|
905
|
+
// engine's own, rather than adding a hand-computed stride.
|
|
906
|
+
ValueAddr = Prop->ContainerPtrToValuePtr<void>(Container, Index);
|
|
907
|
+
}
|
|
908
|
+
else
|
|
909
|
+
{
|
|
910
|
+
OutError = FString::Printf(TEXT("'%s' is not an array but was indexed [%d]"), *Token, Index);
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
604
913
|
}
|
|
605
914
|
|
|
606
915
|
if (i == Parts.Num() - 1)
|
|
@@ -614,8 +923,25 @@ namespace MCPJsonProperty
|
|
|
614
923
|
// Descend for the next token.
|
|
615
924
|
if (FStructProperty* SP = CastField<FStructProperty>(Prop))
|
|
616
925
|
{
|
|
617
|
-
|
|
618
|
-
|
|
926
|
+
if (SP->Struct == FInstancedStruct::StaticStruct())
|
|
927
|
+
{
|
|
928
|
+
FInstancedStruct* InstancedStruct = static_cast<FInstancedStruct*>(ValueAddr);
|
|
929
|
+
const UScriptStruct* PayloadStruct = InstancedStruct->GetScriptStruct();
|
|
930
|
+
void* PayloadMemory = InstancedStruct->GetMutableMemory();
|
|
931
|
+
if (!PayloadStruct || !PayloadMemory)
|
|
932
|
+
{
|
|
933
|
+
OutError = FString::Printf(TEXT("'%s' FInstancedStruct payload is empty - cannot descend"), *PathToken);
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
Container = PayloadMemory;
|
|
938
|
+
ContainerStruct = PayloadStruct;
|
|
939
|
+
}
|
|
940
|
+
else
|
|
941
|
+
{
|
|
942
|
+
Container = ValueAddr;
|
|
943
|
+
ContainerStruct = SP->Struct;
|
|
944
|
+
}
|
|
619
945
|
}
|
|
620
946
|
else if (FObjectProperty* OP = CastField<FObjectProperty>(Prop))
|
|
621
947
|
{
|