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
|
@@ -73,6 +73,8 @@ void FGasHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
73
73
|
Registry.RegisterHandler(TEXT("get_attribute"), &GetAttribute);
|
|
74
74
|
Registry.RegisterHandler(TEXT("init_asc"), &InitAsc);
|
|
75
75
|
Registry.RegisterHandler(TEXT("get_asc_state"), &GetAscState);
|
|
76
|
+
Registry.RegisterHandler(TEXT("get_live_attribute_value"), &GetLiveAttributeValue);
|
|
77
|
+
Registry.RegisterHandler(TEXT("set_live_attribute_value"), &SetLiveAttributeValue);
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
TSharedPtr<FJsonValue> FGasHandlers::CreateGasBlueprint(
|
|
@@ -5,6 +5,54 @@
|
|
|
5
5
|
#include "Dom/JsonObject.h"
|
|
6
6
|
#include "Templates/Function.h"
|
|
7
7
|
|
|
8
|
+
class AActor;
|
|
9
|
+
class UAbilitySystemComponent;
|
|
10
|
+
class UClass;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Attribute set registration on a live AbilitySystemComponent (#956).
|
|
14
|
+
*
|
|
15
|
+
* Declared here rather than kept file-local so the automation tests can pin
|
|
16
|
+
* the one invariant the live-attribute actions stand on, and so there is
|
|
17
|
+
* exactly one copy: the module is a unity build and a second file-local copy
|
|
18
|
+
* of a helper is a redefinition on some grouping.
|
|
19
|
+
*/
|
|
20
|
+
namespace MCPGas
|
|
21
|
+
{
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register the actor's own UAttributeSet subobjects on its ASC, the way
|
|
25
|
+
* UAbilitySystemComponent::InitializeComponent does at BeginPlay.
|
|
26
|
+
*
|
|
27
|
+
* This exists because an actor spawned into the pure editor world never runs
|
|
28
|
+
* that scan: there is no BeginPlay and no InitializeComponent in a world that
|
|
29
|
+
* has not begun play. The ASC therefore has ZERO registered sets while the
|
|
30
|
+
* actor plainly owns one, and the obvious fallback at that point ("the ASC has
|
|
31
|
+
* none, so construct one") produces a SECOND, disconnected instance that no
|
|
32
|
+
* gameplay code will ever consult. Adopting the actor's existing subobject is
|
|
33
|
+
* the only answer that leaves ASC->GetSet<T>() pointing at the real one.
|
|
34
|
+
*
|
|
35
|
+
* Nested subobjects are included, unlike the engine's own scan, so a set
|
|
36
|
+
* created as a subobject of the ASC rather than of the actor is found too.
|
|
37
|
+
* Idempotent. Returns how many were newly registered.
|
|
38
|
+
*/
|
|
39
|
+
int32 AdoptOwnerAttributeSets(
|
|
40
|
+
UAbilitySystemComponent* ASC,
|
|
41
|
+
AActor* Actor,
|
|
42
|
+
TArray<FString>& OutAdoptedClassNames);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The FGameplayAttributeData property named on one attribute set class.
|
|
46
|
+
* Accepts a bare property name ("Mana") and the qualified spellings
|
|
47
|
+
* ("MySet.Mana" / "MySet:Mana") the other GAS actions take. Null on a miss.
|
|
48
|
+
*/
|
|
49
|
+
FStructProperty* FindAttributeDataProperty(UClass* SetClass, const FString& Name);
|
|
50
|
+
|
|
51
|
+
/** Every FGameplayAttributeData property name on a set class, comma separated. */
|
|
52
|
+
FString ListAttributeDataPropertyNames(UClass* SetClass);
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
8
56
|
class FGasHandlers
|
|
9
57
|
{
|
|
10
58
|
public:
|
|
@@ -48,4 +96,16 @@ private:
|
|
|
48
96
|
// #587: introspect a live ASC - granted ability specs (class, level, input,
|
|
49
97
|
// active, dynamic tags) + owned gameplay tags.
|
|
50
98
|
static TSharedPtr<FJsonValue> GetAscState(const TSharedPtr<FJsonObject>& Params);
|
|
99
|
+
|
|
100
|
+
// #956: read and write the CURRENT value of one FGameplayAttributeData on
|
|
101
|
+
// the attribute set instance actually REGISTERED on a live actor's ASC.
|
|
102
|
+
// get_attribute / set_attribute above only see sets the ASC already knows
|
|
103
|
+
// about, and an actor spawned into the editor world has none, because the
|
|
104
|
+
// DSO scan that registers them runs in InitializeComponent and a world that
|
|
105
|
+
// has not begun play never gets there. These two name the set explicitly
|
|
106
|
+
// and reach the registered instance, adopting the actor's own subobject
|
|
107
|
+
// when the ASC has not registered it yet. Implemented in
|
|
108
|
+
// GasHandlers_Runtime.cpp.
|
|
109
|
+
static TSharedPtr<FJsonValue> GetLiveAttributeValue(const TSharedPtr<FJsonObject>& Params);
|
|
110
|
+
static TSharedPtr<FJsonValue> SetLiveAttributeValue(const TSharedPtr<FJsonObject>& Params);
|
|
51
111
|
};
|
|
@@ -16,6 +16,75 @@
|
|
|
16
16
|
#include "GameplayEffect.h"
|
|
17
17
|
#include "GameplayEffectTypes.h"
|
|
18
18
|
#include "GameplayTagContainer.h"
|
|
19
|
+
#include "UObject/UObjectHash.h"
|
|
20
|
+
#include "UObject/UnrealType.h"
|
|
21
|
+
|
|
22
|
+
namespace MCPGas
|
|
23
|
+
{
|
|
24
|
+
|
|
25
|
+
int32 AdoptOwnerAttributeSets(
|
|
26
|
+
UAbilitySystemComponent* ASC,
|
|
27
|
+
AActor* Actor,
|
|
28
|
+
TArray<FString>& OutAdoptedClassNames)
|
|
29
|
+
{
|
|
30
|
+
if (!ASC || !Actor) return 0;
|
|
31
|
+
|
|
32
|
+
TArray<UObject*> Subobjects;
|
|
33
|
+
GetObjectsWithOuter(Actor, Subobjects);
|
|
34
|
+
|
|
35
|
+
int32 Adopted = 0;
|
|
36
|
+
for (UObject* Object : Subobjects)
|
|
37
|
+
{
|
|
38
|
+
UAttributeSet* Set = Cast<UAttributeSet>(Object);
|
|
39
|
+
if (!IsValid(Set)) continue;
|
|
40
|
+
// Already registered: leave the existing entry alone. Re-adding would
|
|
41
|
+
// not duplicate it, but skipping keeps the "newly registered" count
|
|
42
|
+
// honest, and that count is what the result reports.
|
|
43
|
+
if (ASC->GetSpawnedAttributes().Contains(Set)) continue;
|
|
44
|
+
ASC->AddSpawnedAttribute(Set);
|
|
45
|
+
OutAdoptedClassNames.Add(Set->GetClass()->GetName());
|
|
46
|
+
++Adopted;
|
|
47
|
+
}
|
|
48
|
+
return Adopted;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
FStructProperty* FindAttributeDataProperty(UClass* SetClass, const FString& Name)
|
|
52
|
+
{
|
|
53
|
+
if (!SetClass) return nullptr;
|
|
54
|
+
const FString SetName = SetClass->GetName();
|
|
55
|
+
for (TFieldIterator<FProperty> It(SetClass); It; ++It)
|
|
56
|
+
{
|
|
57
|
+
FStructProperty* SProp = CastField<FStructProperty>(*It);
|
|
58
|
+
if (!SProp || SProp->Struct != FGameplayAttributeData::StaticStruct()) continue;
|
|
59
|
+
const FString PropName = SProp->GetName();
|
|
60
|
+
if (PropName == Name
|
|
61
|
+
|| (SetName + TEXT(".") + PropName) == Name
|
|
62
|
+
|| (SetName + TEXT(":") + PropName) == Name)
|
|
63
|
+
{
|
|
64
|
+
return SProp;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return nullptr;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
FString ListAttributeDataPropertyNames(UClass* SetClass)
|
|
71
|
+
{
|
|
72
|
+
TArray<FString> Names;
|
|
73
|
+
if (SetClass)
|
|
74
|
+
{
|
|
75
|
+
for (TFieldIterator<FProperty> It(SetClass); It; ++It)
|
|
76
|
+
{
|
|
77
|
+
FStructProperty* SProp = CastField<FStructProperty>(*It);
|
|
78
|
+
if (SProp && SProp->Struct == FGameplayAttributeData::StaticStruct())
|
|
79
|
+
{
|
|
80
|
+
Names.Add(SProp->GetName());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return Names.Num() > 0 ? FString::Join(Names, TEXT(", ")) : FString(TEXT("(none)"));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
19
88
|
|
|
20
89
|
namespace
|
|
21
90
|
{
|
|
@@ -26,7 +95,7 @@ namespace
|
|
|
26
95
|
return ResolveWorldScope(OptionalString(Params, TEXT("world"), TEXT("auto")));
|
|
27
96
|
}
|
|
28
97
|
|
|
29
|
-
// Find the actor
|
|
98
|
+
// Find the actor in the resolved world and return its
|
|
30
99
|
// AbilitySystemComponent. On any failure writes a structured error to
|
|
31
100
|
// OutError and returns nullptr.
|
|
32
101
|
UAbilitySystemComponent* ResolveASC(
|
|
@@ -35,7 +104,7 @@ namespace
|
|
|
35
104
|
TSharedPtr<FJsonValue>& OutError)
|
|
36
105
|
{
|
|
37
106
|
FString ActorLabel;
|
|
38
|
-
if (auto Err =
|
|
107
|
+
if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel))
|
|
39
108
|
{
|
|
40
109
|
OutError = Err;
|
|
41
110
|
return nullptr;
|
|
@@ -48,12 +117,17 @@ namespace
|
|
|
48
117
|
return nullptr;
|
|
49
118
|
}
|
|
50
119
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
120
|
+
// #956: label, internal name, or full object path, in that fixed order.
|
|
121
|
+
// A verification actor spawned into the editor world often has no label
|
|
122
|
+
// worth guessing, so the path has to be a first-class way to name it.
|
|
123
|
+
// #983: actorPath is its own parameter now, and a label that names more
|
|
124
|
+
// than one actor is refused rather than answered from one of them.
|
|
125
|
+
FMCPActorSelector ActorSel;
|
|
126
|
+
ActorSel.Match = EMCPActorMatch::LabelNameOrPath;
|
|
127
|
+
ActorSel.WorldLabel = World->IsPlayInEditor() ? TEXT("PIE") : TEXT("editor");
|
|
128
|
+
AActor* Actor = MCPResolveActor(World, Params, OutError, ActorSel);
|
|
129
|
+
if (!Actor) return nullptr;
|
|
130
|
+
ActorLabel = Actor->GetActorLabel();
|
|
57
131
|
OutActor = Actor;
|
|
58
132
|
|
|
59
133
|
UAbilitySystemComponent* ASC =
|
|
@@ -109,6 +183,76 @@ namespace
|
|
|
109
183
|
Obj->SetNumberField(TEXT("currentValue"), ASC->GetNumericAttribute(Attr));
|
|
110
184
|
}
|
|
111
185
|
|
|
186
|
+
// ── Registered attribute sets (#956) ──────────────────────────────
|
|
187
|
+
//
|
|
188
|
+
// UAbilitySystemComponent::GetAttributeSet / GetSet<T>() search
|
|
189
|
+
// SpawnedAttributes, and SpawnedAttributes IS the registry the gameplay
|
|
190
|
+
// code consults. Nothing else is. An actor's own UPROPERTY pointer to a
|
|
191
|
+
// CreateDefaultSubobject-created set is NOT proof the ASC knows about it.
|
|
192
|
+
//
|
|
193
|
+
// Those two only agree once InitializeComponent has run. It is
|
|
194
|
+
// InitializeComponent that scans the owner's default subobjects and calls
|
|
195
|
+
// AddSpawnedAttribute on every UAttributeSet it finds, and an actor spawned
|
|
196
|
+
// into the pure editor world never reaches it: there is no BeginPlay and no
|
|
197
|
+
// InitializeComponent in a world that has not begun play. So in the editor
|
|
198
|
+
// world the ASC has ZERO registered sets while the actor plainly has one.
|
|
199
|
+
//
|
|
200
|
+
// The trap that follows, and the reason this comment is this long: the
|
|
201
|
+
// obvious fallback at that point is "the ASC has none, so make one", which
|
|
202
|
+
// constructs a SECOND, disconnected instance next to the actor's own. Every
|
|
203
|
+
// read and write then lands on an object no gameplay code will ever look
|
|
204
|
+
// at, no error is reported, and the verification says the change worked. So
|
|
205
|
+
// we adopt the actor's EXISTING subobject into the ASC instead, which is
|
|
206
|
+
// exactly what InitializeComponent would have done, and we only ever hand
|
|
207
|
+
// back what GetAttributeSet returns afterwards.
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The instance the gameplay code consults, and nothing else.
|
|
211
|
+
*
|
|
212
|
+
* Always answered by ASC->GetAttributeSet (the non-template form of
|
|
213
|
+
* ASC->GetSet<T>()). When the ASC has not registered one and adoption is
|
|
214
|
+
* allowed, the actor's own subobject is registered first and the ASC is
|
|
215
|
+
* asked again, so the pointer that comes back is still the registered one.
|
|
216
|
+
* A new set is never constructed here.
|
|
217
|
+
*/
|
|
218
|
+
UAttributeSet* ResolveRegisteredAttributeSet(
|
|
219
|
+
UAbilitySystemComponent* ASC,
|
|
220
|
+
AActor* Actor,
|
|
221
|
+
UClass* SetClass,
|
|
222
|
+
bool bAllowAdopt,
|
|
223
|
+
bool& bOutAdopted,
|
|
224
|
+
TArray<FString>& OutAdoptedClassNames,
|
|
225
|
+
TSharedPtr<FJsonValue>& OutError)
|
|
226
|
+
{
|
|
227
|
+
bOutAdopted = false;
|
|
228
|
+
if (const UAttributeSet* Registered = ASC->GetAttributeSet(SetClass))
|
|
229
|
+
{
|
|
230
|
+
// const_cast is the only way to a mutable registered set: the ASC
|
|
231
|
+
// hands out const pointers and keeps no non-const accessor. The
|
|
232
|
+
// object is not const, only the view of it.
|
|
233
|
+
return const_cast<UAttributeSet*>(Registered);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (bAllowAdopt && MCPGas::AdoptOwnerAttributeSets(ASC, Actor, OutAdoptedClassNames) > 0)
|
|
237
|
+
{
|
|
238
|
+
if (const UAttributeSet* Registered = ASC->GetAttributeSet(SetClass))
|
|
239
|
+
{
|
|
240
|
+
bOutAdopted = true;
|
|
241
|
+
return const_cast<UAttributeSet*>(Registered);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
OutError = MCPError(FString::Printf(
|
|
246
|
+
TEXT("No '%s' is registered on '%s' AbilitySystemComponent%s. ")
|
|
247
|
+
TEXT("A set the actor owns is only registered once InitializeComponent runs, which never happens in a world that has not begun play. ")
|
|
248
|
+
TEXT("Start PIE, or call gas(action=\"init_asc\") with attributeSet=\"%s\" to register it."),
|
|
249
|
+
*SetClass->GetName(),
|
|
250
|
+
*Actor->GetActorLabel(),
|
|
251
|
+
bAllowAdopt ? TEXT(", and the actor owns no instance of it either") : TEXT(" (registerOwnerSets was false)"),
|
|
252
|
+
*SetClass->GetName()));
|
|
253
|
+
return nullptr;
|
|
254
|
+
}
|
|
255
|
+
|
|
112
256
|
// Resolve a UClass deriving from Base from a content path or short class name.
|
|
113
257
|
// Handles native classes, Blueprint generated classes (path + "_C"), and a
|
|
114
258
|
// Blueprint-asset fallback. Returns nullptr unless the result is a Base subclass.
|
|
@@ -298,6 +442,191 @@ TSharedPtr<FJsonValue> FGasHandlers::GetAttribute(const TSharedPtr<FJsonObject>&
|
|
|
298
442
|
return MCPResult(Result);
|
|
299
443
|
}
|
|
300
444
|
|
|
445
|
+
// ── Live attribute values on the REGISTERED set (#956) ───────────────────────
|
|
446
|
+
//
|
|
447
|
+
// gas(get_attribute) / gas(set_attribute) walk whatever the ASC already has
|
|
448
|
+
// registered and match by attribute name. That is the right shape once a game
|
|
449
|
+
// has begun play and its sets are registered. It answers nothing at all for an
|
|
450
|
+
// actor spawned into the editor world for a verification pass, because nothing
|
|
451
|
+
// is registered there yet.
|
|
452
|
+
//
|
|
453
|
+
// These two name the attribute set explicitly, resolve the instance THROUGH THE
|
|
454
|
+
// ASC, and register the actor's own subobject when the ASC has not (see the
|
|
455
|
+
// long note on ResolveRegisteredAttributeSet above for why constructing a new
|
|
456
|
+
// set instead is the trap that silently reads and writes an object no gameplay
|
|
457
|
+
// code consults). Both report the registered instance's object path, so a
|
|
458
|
+
// caller can prove which object was touched.
|
|
459
|
+
|
|
460
|
+
namespace
|
|
461
|
+
{
|
|
462
|
+
/** Params shared by both live-attribute actions. */
|
|
463
|
+
struct FLiveAttributeRequest
|
|
464
|
+
{
|
|
465
|
+
AActor* Actor = nullptr;
|
|
466
|
+
UAbilitySystemComponent* ASC = nullptr;
|
|
467
|
+
UAttributeSet* Set = nullptr;
|
|
468
|
+
FGameplayAttribute Attribute;
|
|
469
|
+
bool bAdopted = false;
|
|
470
|
+
TArray<FString> AdoptedClassNames;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
/** Everything both actions do before they diverge. */
|
|
474
|
+
bool ResolveLiveAttributeRequest(
|
|
475
|
+
const TSharedPtr<FJsonObject>& Params,
|
|
476
|
+
FLiveAttributeRequest& Out,
|
|
477
|
+
TSharedPtr<FJsonValue>& OutError)
|
|
478
|
+
{
|
|
479
|
+
FString SetSpec;
|
|
480
|
+
if (auto Err = RequireString(Params, TEXT("attributeSet"), SetSpec))
|
|
481
|
+
{
|
|
482
|
+
OutError = Err;
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
FString AttrName;
|
|
486
|
+
if (auto Err = RequireString(Params, TEXT("attribute"), AttrName))
|
|
487
|
+
{
|
|
488
|
+
OutError = Err;
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
Out.ASC = ResolveASC(Params, Out.Actor, OutError);
|
|
493
|
+
if (!Out.ASC) return false;
|
|
494
|
+
|
|
495
|
+
UClass* SetClass = ResolveClassDeriving(SetSpec, UAttributeSet::StaticClass());
|
|
496
|
+
if (!SetClass)
|
|
497
|
+
{
|
|
498
|
+
OutError = MCPError(FString::Printf(
|
|
499
|
+
TEXT("AttributeSet class not found: %s (pass a content path or a class name)"), *SetSpec));
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const bool bAllowAdopt = OptionalBool(Params, TEXT("registerOwnerSets"), true);
|
|
504
|
+
Out.Set = ResolveRegisteredAttributeSet(
|
|
505
|
+
Out.ASC, Out.Actor, SetClass, bAllowAdopt, Out.bAdopted, Out.AdoptedClassNames, OutError);
|
|
506
|
+
if (!Out.Set) return false;
|
|
507
|
+
|
|
508
|
+
// Match against the REGISTERED instance's class, not the requested one:
|
|
509
|
+
// the registered set may be a subclass of what the caller named, and its
|
|
510
|
+
// own properties are the ones the aggregator reads.
|
|
511
|
+
FStructProperty* AttrProp = MCPGas::FindAttributeDataProperty(Out.Set->GetClass(), AttrName);
|
|
512
|
+
if (!AttrProp)
|
|
513
|
+
{
|
|
514
|
+
OutError = MCPError(FString::Printf(
|
|
515
|
+
TEXT("Attribute '%s' not found on '%s'. Available: %s"),
|
|
516
|
+
*AttrName,
|
|
517
|
+
*Out.Set->GetClass()->GetName(),
|
|
518
|
+
*MCPGas::ListAttributeDataPropertyNames(Out.Set->GetClass())));
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
Out.Attribute = FGameplayAttribute(AttrProp);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Identity + both values of the attribute, read off the registered set. */
|
|
526
|
+
void WriteLiveAttributeFields(TSharedPtr<FJsonObject> Obj, const FLiveAttributeRequest& Req)
|
|
527
|
+
{
|
|
528
|
+
Obj->SetStringField(TEXT("actorLabel"), Req.Actor->GetActorLabel());
|
|
529
|
+
Obj->SetStringField(TEXT("actorPath"), Req.Actor->GetPathName());
|
|
530
|
+
Obj->SetStringField(TEXT("attributeSet"), Req.Set->GetClass()->GetName());
|
|
531
|
+
// The proof of which object was touched. A second, disconnected instance
|
|
532
|
+
// would show a different path here.
|
|
533
|
+
Obj->SetStringField(TEXT("attributeSetInstance"), Req.Set->GetPathName());
|
|
534
|
+
Obj->SetStringField(TEXT("attribute"), Req.Attribute.GetName());
|
|
535
|
+
Obj->SetBoolField(TEXT("registeredOnAsc"), true);
|
|
536
|
+
Obj->SetBoolField(TEXT("registeredByThisCall"), Req.bAdopted);
|
|
537
|
+
if (Req.AdoptedClassNames.Num() > 0)
|
|
538
|
+
{
|
|
539
|
+
TArray<TSharedPtr<FJsonValue>> Adopted;
|
|
540
|
+
for (const FString& Name : Req.AdoptedClassNames)
|
|
541
|
+
{
|
|
542
|
+
Adopted.Add(MakeShared<FJsonValueString>(Name));
|
|
543
|
+
}
|
|
544
|
+
Obj->SetArrayField(TEXT("registeredSets"), Adopted);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Read straight off the registered instance. GetNumericValue is the
|
|
548
|
+
// engine's own accessor for the current value of the attribute data.
|
|
549
|
+
Obj->SetNumberField(TEXT("currentValue"), Req.Attribute.GetNumericValue(Req.Set));
|
|
550
|
+
if (const FGameplayAttributeData* Data = Req.Attribute.GetGameplayAttributeData(Req.Set))
|
|
551
|
+
{
|
|
552
|
+
Obj->SetNumberField(TEXT("baseValue"), Data->GetBaseValue());
|
|
553
|
+
}
|
|
554
|
+
// The aggregator's view of the same attribute. Identical to the values
|
|
555
|
+
// above until an active GameplayEffect is modifying it, and the pair is
|
|
556
|
+
// what tells you an effect really landed.
|
|
557
|
+
Obj->SetNumberField(TEXT("aggregatorBaseValue"), Req.ASC->GetNumericAttributeBase(Req.Attribute));
|
|
558
|
+
Obj->SetNumberField(TEXT("aggregatorCurrentValue"), Req.ASC->GetNumericAttribute(Req.Attribute));
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
TSharedPtr<FJsonValue> FGasHandlers::GetLiveAttributeValue(const TSharedPtr<FJsonObject>& Params)
|
|
563
|
+
{
|
|
564
|
+
MCP_CHECK_GAME_THREAD();
|
|
565
|
+
|
|
566
|
+
FLiveAttributeRequest Req;
|
|
567
|
+
TSharedPtr<FJsonValue> Err;
|
|
568
|
+
if (!ResolveLiveAttributeRequest(Params, Req, Err)) return Err;
|
|
569
|
+
|
|
570
|
+
auto Result = MCPSuccess();
|
|
571
|
+
WriteLiveAttributeFields(Result, Req);
|
|
572
|
+
return MCPResult(Result);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
TSharedPtr<FJsonValue> FGasHandlers::SetLiveAttributeValue(const TSharedPtr<FJsonObject>& Params)
|
|
576
|
+
{
|
|
577
|
+
MCP_CHECK_GAME_THREAD();
|
|
578
|
+
|
|
579
|
+
double NewValue = 0.0;
|
|
580
|
+
if (!Params->TryGetNumberField(TEXT("value"), NewValue))
|
|
581
|
+
{
|
|
582
|
+
return MCPError(TEXT("Missing required parameter 'value'"));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// "current" writes the FGameplayAttributeData's current value in place,
|
|
586
|
+
// which is what verifying a mid-combat state needs. "base" goes through the
|
|
587
|
+
// ASC so the aggregator recomputes the current value from it, which is what
|
|
588
|
+
// a durable change needs. They are not interchangeable, so the caller says.
|
|
589
|
+
const FString ValueType = OptionalString(Params, TEXT("valueType"), TEXT("current")).ToLower();
|
|
590
|
+
if (ValueType != TEXT("current") && ValueType != TEXT("base"))
|
|
591
|
+
{
|
|
592
|
+
return MCPError(FString::Printf(
|
|
593
|
+
TEXT("Unknown valueType '%s'. Use \"current\" (write the attribute data in place) or \"base\" (write through the ASC aggregator)."),
|
|
594
|
+
*ValueType));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
FLiveAttributeRequest Req;
|
|
598
|
+
TSharedPtr<FJsonValue> Err;
|
|
599
|
+
if (!ResolveLiveAttributeRequest(Params, Req, Err)) return Err;
|
|
600
|
+
|
|
601
|
+
const float PreviousCurrent = Req.Attribute.GetNumericValue(Req.Set);
|
|
602
|
+
const float PreviousBase = Req.ASC->GetNumericAttributeBase(Req.Attribute);
|
|
603
|
+
|
|
604
|
+
if (ValueType == TEXT("base"))
|
|
605
|
+
{
|
|
606
|
+
// Recalculates the current value through the aggregator, so active
|
|
607
|
+
// modifiers stay consistent.
|
|
608
|
+
Req.ASC->SetNumericAttributeBase(Req.Attribute, static_cast<float>(NewValue));
|
|
609
|
+
}
|
|
610
|
+
else
|
|
611
|
+
{
|
|
612
|
+
// SetNumericValueChecked takes a mutable reference because the set's
|
|
613
|
+
// PreAttributeChange is allowed to clamp the value, so the number that
|
|
614
|
+
// lands can differ from the number asked for. That is why the result
|
|
615
|
+
// reports what was actually stored rather than echoing the request.
|
|
616
|
+
float Applied = static_cast<float>(NewValue);
|
|
617
|
+
Req.Attribute.SetNumericValueChecked(Applied, Req.Set);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
auto Result = MCPSuccess();
|
|
621
|
+
MCPSetUpdated(Result);
|
|
622
|
+
WriteLiveAttributeFields(Result, Req);
|
|
623
|
+
Result->SetStringField(TEXT("valueType"), ValueType);
|
|
624
|
+
Result->SetNumberField(TEXT("requestedValue"), NewValue);
|
|
625
|
+
Result->SetNumberField(TEXT("previousCurrentValue"), PreviousCurrent);
|
|
626
|
+
Result->SetNumberField(TEXT("previousBaseValue"), PreviousBase);
|
|
627
|
+
return MCPResult(Result);
|
|
628
|
+
}
|
|
629
|
+
|
|
301
630
|
// #587 get_asc_state - introspect a live ASC: granted ability specs (class,
|
|
302
631
|
// level, input id, active state, dynamic source tags) plus the ASC's owned
|
|
303
632
|
// gameplay tags. The read half of #587 (input injection lives in pie-studio).
|
|
@@ -362,12 +691,21 @@ TSharedPtr<FJsonValue> FGasHandlers::InitAsc(const TSharedPtr<FJsonObject>& Para
|
|
|
362
691
|
// correctly. Safe to call again; a game's own pawn may also init the ASC.
|
|
363
692
|
ASC->InitAbilityActorInfo(Actor, Actor);
|
|
364
693
|
|
|
694
|
+
// #956: register the actor's OWN attribute set subobjects first, the way
|
|
695
|
+
// InitializeComponent would have at BeginPlay. Skipping this step and going
|
|
696
|
+
// straight to "the ASC has none, so construct one" is what produced a
|
|
697
|
+
// second, disconnected instance next to the actor's own: reads and writes
|
|
698
|
+
// then landed on an object the gameplay code never consults, and nothing
|
|
699
|
+
// reported an error. Adoption is idempotent and costs one hash walk.
|
|
700
|
+
TArray<FString> AdoptedSets;
|
|
701
|
+
MCPGas::AdoptOwnerAttributeSets(ASC, Actor, AdoptedSets);
|
|
702
|
+
|
|
365
703
|
// Optionally guarantee an attribute set exists on the ASC. This is what lets
|
|
366
704
|
// a bridge-authored test actor have live attributes without shipping an init
|
|
367
|
-
// DataTable
|
|
368
|
-
//
|
|
369
|
-
// the public GetAttributeSet + AddSpawnedAttribute pair.
|
|
705
|
+
// DataTable. A set is only constructed when the actor genuinely owns none of
|
|
706
|
+
// that class, and the result says which of the two happened.
|
|
370
707
|
FString CreatedSet;
|
|
708
|
+
bool bConstructedSet = false;
|
|
371
709
|
const FString AttrSetSpec = OptionalString(Params, TEXT("attributeSet"));
|
|
372
710
|
if (!AttrSetSpec.IsEmpty())
|
|
373
711
|
{
|
|
@@ -383,6 +721,7 @@ TSharedPtr<FJsonValue> FGasHandlers::InitAsc(const TSharedPtr<FJsonObject>& Para
|
|
|
383
721
|
UAttributeSet* NewSet = NewObject<UAttributeSet>(Actor, AttrSetClass);
|
|
384
722
|
ASC->AddSpawnedAttribute(NewSet);
|
|
385
723
|
CreatedSet = NewSet->GetClass()->GetName();
|
|
724
|
+
bConstructedSet = true;
|
|
386
725
|
}
|
|
387
726
|
else
|
|
388
727
|
{
|
|
@@ -405,7 +744,19 @@ TSharedPtr<FJsonValue> FGasHandlers::InitAsc(const TSharedPtr<FJsonObject>& Para
|
|
|
405
744
|
auto Result = MCPSuccess();
|
|
406
745
|
Result->SetStringField(TEXT("actorLabel"), Actor->GetActorLabel());
|
|
407
746
|
Result->SetBoolField(TEXT("initialized"), true);
|
|
408
|
-
if (!CreatedSet.IsEmpty())
|
|
747
|
+
if (!CreatedSet.IsEmpty())
|
|
748
|
+
{
|
|
749
|
+
Result->SetStringField(TEXT("attributeSet"), CreatedSet);
|
|
750
|
+
// The caller needs to know which happened. A constructed set starts at
|
|
751
|
+
// its class defaults; an adopted one carries whatever the actor has.
|
|
752
|
+
Result->SetBoolField(TEXT("attributeSetConstructed"), bConstructedSet);
|
|
753
|
+
}
|
|
754
|
+
if (AdoptedSets.Num() > 0)
|
|
755
|
+
{
|
|
756
|
+
TArray<TSharedPtr<FJsonValue>> Adopted;
|
|
757
|
+
for (const FString& Name : AdoptedSets) Adopted.Add(MakeShared<FJsonValueString>(Name));
|
|
758
|
+
Result->SetArrayField(TEXT("registeredOwnerSets"), Adopted);
|
|
759
|
+
}
|
|
409
760
|
Result->SetNumberField(TEXT("attributeCount"), AttrCount);
|
|
410
761
|
return MCPResult(Result);
|
|
411
762
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Editor-state helpers shared by more than one handler translation unit.
|
|
4
|
+
//
|
|
5
|
+
// The module is compiled as a unity build, so a file-local helper copied into a
|
|
6
|
+
// second .cpp becomes a redefinition the moment UBT groups the two files into
|
|
7
|
+
// one blob (error C2084), and the grouping is not stable across machines.
|
|
8
|
+
// Anything two handlers both need lives here, in a named namespace, as an
|
|
9
|
+
// inline function. It is a Private header rather than Public/HandlerUtils.h
|
|
10
|
+
// because it pulls in the editor file utilities, and HandlerUtils.h is also
|
|
11
|
+
// included by UE_MCP_BridgeStatus, which does not link UnrealEd.
|
|
12
|
+
|
|
13
|
+
#include "CoreMinimal.h"
|
|
14
|
+
#include "Dom/JsonObject.h"
|
|
15
|
+
#include "Dom/JsonValue.h"
|
|
16
|
+
#include "Editor.h"
|
|
17
|
+
#include "Engine/World.h"
|
|
18
|
+
#include "FileHelpers.h"
|
|
19
|
+
#include "Misc/PackageName.h"
|
|
20
|
+
#include "UObject/Package.h"
|
|
21
|
+
|
|
22
|
+
namespace MCPEditorState
|
|
23
|
+
{
|
|
24
|
+
/**
|
|
25
|
+
* Long package names of every dirty content or map package in the editor.
|
|
26
|
+
*
|
|
27
|
+
* Two handlers need this for the same reason: an operation that temporarily
|
|
28
|
+
* opens a different map has to refuse when the editor already has unsaved
|
|
29
|
+
* work, because loading another level either discards it or raises a modal
|
|
30
|
+
* save prompt the bridge cannot answer.
|
|
31
|
+
*
|
|
32
|
+
* `/Script/` packages are always dirty-ish bookkeeping and are excluded.
|
|
33
|
+
*/
|
|
34
|
+
inline void CollectDirtyEditorPackageNames(TArray<FString>& OutPackageNames)
|
|
35
|
+
{
|
|
36
|
+
OutPackageNames.Reset();
|
|
37
|
+
TArray<UPackage*> DirtyPackages;
|
|
38
|
+
FEditorFileUtils::GetDirtyContentPackages(DirtyPackages);
|
|
39
|
+
FEditorFileUtils::GetDirtyWorldPackages(DirtyPackages);
|
|
40
|
+
for (UPackage* Package : DirtyPackages)
|
|
41
|
+
{
|
|
42
|
+
if (!Package || !Package->IsDirty())
|
|
43
|
+
{
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const FString PackageName = Package->GetName();
|
|
48
|
+
if (PackageName.StartsWith(TEXT("/Script/")))
|
|
49
|
+
{
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
OutPackageNames.AddUnique(PackageName);
|
|
54
|
+
}
|
|
55
|
+
OutPackageNames.Sort();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Long package name of the map currently open in the editor, or empty. */
|
|
59
|
+
inline FString CurrentEditorLevelPackageName()
|
|
60
|
+
{
|
|
61
|
+
if (!GEditor)
|
|
62
|
+
{
|
|
63
|
+
return FString();
|
|
64
|
+
}
|
|
65
|
+
UWorld* World = GEditor->GetEditorWorldContext().World();
|
|
66
|
+
if (!World || !World->GetOutermost())
|
|
67
|
+
{
|
|
68
|
+
return FString();
|
|
69
|
+
}
|
|
70
|
+
return World->GetOutermost()->GetName();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Is this long package name an existing .umap on disk? */
|
|
74
|
+
inline bool IsExistingMapPackage(const FString& LevelPath)
|
|
75
|
+
{
|
|
76
|
+
if (LevelPath.IsEmpty() || !FPackageName::IsValidLongPackageName(LevelPath))
|
|
77
|
+
{
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
FString Filename;
|
|
81
|
+
return FPackageName::DoesPackageExist(LevelPath, &Filename) &&
|
|
82
|
+
Filename.EndsWith(FPackageName::GetMapPackageExtension(), ESearchCase::IgnoreCase);
|
|
83
|
+
}
|
|
84
|
+
}
|