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
package/dist/editor-target.js
CHANGED
|
@@ -25,6 +25,58 @@ import * as path from "node:path";
|
|
|
25
25
|
export function bridgeLockfilePath(projectDir) {
|
|
26
26
|
return path.join(projectDir, "Saved", "UE_MCP_Bridge", "port.json");
|
|
27
27
|
}
|
|
28
|
+
/** Where every live bridge publishes its own address, one file per process. */
|
|
29
|
+
export function bridgeInstancesDir(projectDir) {
|
|
30
|
+
return path.join(projectDir, "Saved", "UE_MCP_Bridge", "instances");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Every readable instance record for this project, newest first.
|
|
34
|
+
*
|
|
35
|
+
* Records are read, not trusted: the caller decides which pids are still alive.
|
|
36
|
+
* A record whose process is gone is exactly the artefact that outlives a crash,
|
|
37
|
+
* and treating it as an address would resurrect the bug the lockfile pid check
|
|
38
|
+
* exists to prevent (#819).
|
|
39
|
+
*/
|
|
40
|
+
export function readBridgeInstanceRecords(projectDir) {
|
|
41
|
+
const dir = bridgeInstancesDir(projectDir);
|
|
42
|
+
let names;
|
|
43
|
+
try {
|
|
44
|
+
names = fs.readdirSync(dir);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const records = [];
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
if (!name.toLowerCase().endsWith(".json"))
|
|
52
|
+
continue;
|
|
53
|
+
const recordPath = path.join(dir, name);
|
|
54
|
+
try {
|
|
55
|
+
const stat = fs.statSync(recordPath);
|
|
56
|
+
const parsed = JSON.parse(fs.readFileSync(recordPath, "utf-8"));
|
|
57
|
+
const pid = parsed.pid;
|
|
58
|
+
const port = parsed.port;
|
|
59
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
60
|
+
continue;
|
|
61
|
+
if (typeof port !== "number" || !Number.isInteger(port) || port <= 0 || port > 65535)
|
|
62
|
+
continue;
|
|
63
|
+
records.push({
|
|
64
|
+
port,
|
|
65
|
+
pid,
|
|
66
|
+
instanceId: typeof parsed.instanceId === "string" ? parsed.instanceId : null,
|
|
67
|
+
state: typeof parsed.state === "string" ? parsed.state : null,
|
|
68
|
+
startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : null,
|
|
69
|
+
recordPath,
|
|
70
|
+
writtenAtMs: stat.mtimeMs,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// A record being written right now, or one from a build that wrote a
|
|
75
|
+
// different shape. Neither is a reason to discard the others.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return records.sort((a, b) => b.writtenAtMs - a.writtenAtMs);
|
|
79
|
+
}
|
|
28
80
|
/** Read a project's bridge lockfile, or null when it is absent or malformed. */
|
|
29
81
|
export function readBridgeLockfileIn(projectDir) {
|
|
30
82
|
const file = bridgeLockfilePath(projectDir);
|
|
@@ -45,12 +97,43 @@ export function readBridgeLockfileIn(projectDir) {
|
|
|
45
97
|
return null;
|
|
46
98
|
}
|
|
47
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* A live editor's own published address for this project, newest first.
|
|
102
|
+
*
|
|
103
|
+
* #934: two editors of one project share a single `Saved/UE_MCP_Bridge/`, and
|
|
104
|
+
* the one that quits removes port.json on its way out. Only the instance that
|
|
105
|
+
* wrote it ever deletes it, so the survivor is left with no shared lockfile at
|
|
106
|
+
* all even though it is listening perfectly well, and recovery used to mean
|
|
107
|
+
* hand-writing port.json back. Its own `instances/<pid>.json` was on disk the
|
|
108
|
+
* whole time and names its port, so recovery is a directory read.
|
|
109
|
+
*
|
|
110
|
+
* `alive` is injected so the fallback is testable without real pids.
|
|
111
|
+
*/
|
|
112
|
+
export function findLiveInstanceRecord(projectDir, alive = isPidAlive) {
|
|
113
|
+
for (const record of readBridgeInstanceRecords(projectDir)) {
|
|
114
|
+
// A bind-failed record exists to explain a failure, not to be dialled.
|
|
115
|
+
if (record.state === "bind-failed")
|
|
116
|
+
continue;
|
|
117
|
+
if (!alive(record.pid))
|
|
118
|
+
continue;
|
|
119
|
+
return record;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
48
123
|
/**
|
|
49
124
|
* The bridge endpoint belonging to `projectDir`, or a reason naming the exact
|
|
50
125
|
* file that was checked. Never returns a port that this project did not
|
|
51
126
|
* publish for itself.
|
|
127
|
+
*
|
|
128
|
+
* The shared lockfile is still the first answer, and callers still check the
|
|
129
|
+
* pid it names for themselves. When there is no lockfile at all, a live
|
|
130
|
+
* instance record for this project is a strictly better answer than a refusal:
|
|
131
|
+
* the editor published it, for this project, and it names a port nobody else
|
|
132
|
+
* can overwrite (#934).
|
|
133
|
+
*
|
|
134
|
+
* `isAlive` is injected so the fallback is testable without real pids.
|
|
52
135
|
*/
|
|
53
|
-
export function resolveBridgeTarget(projectDir) {
|
|
136
|
+
export function resolveBridgeTarget(projectDir, isAlive = isPidAlive) {
|
|
54
137
|
if (!projectDir) {
|
|
55
138
|
return {
|
|
56
139
|
ok: false,
|
|
@@ -61,21 +144,35 @@ export function resolveBridgeTarget(projectDir) {
|
|
|
61
144
|
}
|
|
62
145
|
const lockfilePath = bridgeLockfilePath(projectDir);
|
|
63
146
|
const lockfile = readBridgeLockfileIn(projectDir);
|
|
64
|
-
if (
|
|
147
|
+
if (lockfile) {
|
|
65
148
|
return {
|
|
66
|
-
ok:
|
|
149
|
+
ok: true,
|
|
150
|
+
port: lockfile.port,
|
|
151
|
+
pid: lockfile.pid,
|
|
152
|
+
writtenAtMs: lockfile.writtenAtMs,
|
|
153
|
+
lockfilePath,
|
|
154
|
+
source: "port.json",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const record = findLiveInstanceRecord(projectDir, isAlive);
|
|
158
|
+
if (record) {
|
|
159
|
+
return {
|
|
160
|
+
ok: true,
|
|
161
|
+
port: record.port,
|
|
162
|
+
pid: record.pid,
|
|
163
|
+
writtenAtMs: record.writtenAtMs,
|
|
67
164
|
lockfilePath,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"Lifecycle actions do not guess a port, because a guessed port reaches whichever editor happens to hold it.",
|
|
165
|
+
source: "instance-record",
|
|
166
|
+
recordPath: record.recordPath,
|
|
71
167
|
};
|
|
72
168
|
}
|
|
73
169
|
return {
|
|
74
|
-
ok:
|
|
75
|
-
port: lockfile.port,
|
|
76
|
-
pid: lockfile.pid,
|
|
77
|
-
writtenAtMs: lockfile.writtenAtMs,
|
|
170
|
+
ok: false,
|
|
78
171
|
lockfilePath,
|
|
172
|
+
reason: `No bridge port published at ${lockfilePath}, and no live instance record under ${bridgeInstancesDir(projectDir)}. ` +
|
|
173
|
+
"The editor writes those files while its bridge is listening and removes them when it exits, so either no " +
|
|
174
|
+
"editor is running for this project or its bridge never started. " +
|
|
175
|
+
"Lifecycle actions do not guess a port, because a guessed port reaches whichever editor happens to hold it.",
|
|
79
176
|
};
|
|
80
177
|
}
|
|
81
178
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-target.js","sourceRoot":"","sources":["../src/editor-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC;AACtE,CAAC;AAWD,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,MAAM,IAAI,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAIvD,CAAC;QACF,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC;YACjH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;YACzE,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YACzE,WAAW,EAAE,IAAI,CAAC,OAAO;SAC1B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"editor-target.js","sourceRoot":"","sources":["../src/editor-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,29 @@ export interface EditorProcess {
|
|
|
34
34
|
responding: boolean;
|
|
35
35
|
windowTitle: string | null;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The .uproject argument on a process command line, absolute and resolved.
|
|
39
|
+
*
|
|
40
|
+
* #967/#970/#965: this used to be one lazy regex over the whole command line,
|
|
41
|
+
* `[A-Za-z]:[\\/][^"]*?\.uproject`. `[^"]` matches spaces, so on the unquoted
|
|
42
|
+
* command line Windows reports for a spawned editor
|
|
43
|
+
*
|
|
44
|
+
* C:\...\UnrealEditor.exe C:\work\Demo\Demo.uproject
|
|
45
|
+
*
|
|
46
|
+
* the match started at the drive letter of the EXECUTABLE and ran all the way
|
|
47
|
+
* to the end, and every process came back with a "project path" that was the
|
|
48
|
+
* exe and the project concatenated. Nothing ever equalled that, so
|
|
49
|
+
* editorOwnsProject was false for the very editor that had the project open:
|
|
50
|
+
* stop_editor refused to stop a healthy editor while printing that same
|
|
51
|
+
* concatenation back as its evidence, and get_engine_state reported no process
|
|
52
|
+
* for a project whose editor was answering on the bridge.
|
|
53
|
+
*
|
|
54
|
+
* So: find where the .uproject argument ENDS (a token boundary, not just any
|
|
55
|
+
* occurrence of the extension), then walk back to the last place a path could
|
|
56
|
+
* have begun. That handles the quoted form, the unquoted form, `-Project=` with
|
|
57
|
+
* and without quotes, and POSIX roots, without assuming a path has no spaces.
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractProjectPath(commandLine: string): string | null;
|
|
37
60
|
/**
|
|
38
61
|
* Every UnrealEditor process on this machine, with enough detail to tell an
|
|
39
62
|
* interactive editor for one project apart from a headless shard for another.
|
|
@@ -86,6 +109,18 @@ export interface LogState {
|
|
|
86
109
|
* before the plugin module exists, which is where the "editor launched but the
|
|
87
110
|
* bridge never came up" reports have always come from.
|
|
88
111
|
*/
|
|
112
|
+
/**
|
|
113
|
+
* The engine root the project was last actually opened with, from its own log.
|
|
114
|
+
*
|
|
115
|
+
* Unreal writes `LogInit: Base Directory: <engine>/Engine/Binaries/<Platform>/`
|
|
116
|
+
* in the first few lines of every run, which is the only durable record of
|
|
117
|
+
* which engine tree launched an editor. It survives the editor exiting, so it
|
|
118
|
+
* answers "which engine is this project on" while the editor is stopped, which
|
|
119
|
+
* is exactly when a full rebuild has to be resolved (#959, #974).
|
|
120
|
+
*
|
|
121
|
+
* Returns null when the log is absent, unreadable, or has no such line.
|
|
122
|
+
*/
|
|
123
|
+
export declare function readEngineRootFromLog(projectPath: string | null | undefined): string | null;
|
|
89
124
|
export declare function readLogState(projectPath: string | null | undefined, tailLines?: number): LogState;
|
|
90
125
|
export interface NativeWindow {
|
|
91
126
|
handle: string;
|
|
@@ -107,6 +142,8 @@ export declare function dialogLikeWindows(windows: NativeWindow[]): NativeWindow
|
|
|
107
142
|
export interface EngineSnapshot {
|
|
108
143
|
writtenAt?: string;
|
|
109
144
|
ageSeconds?: number;
|
|
145
|
+
/** Which process this snapshot describes. Absent on plugin builds before #990. */
|
|
146
|
+
pid?: number;
|
|
110
147
|
phase?: string;
|
|
111
148
|
/**
|
|
112
149
|
* Null until the engine loop starts ticking. Startup has no tick loop to
|
|
@@ -138,11 +175,6 @@ export interface EngineSnapshot {
|
|
|
138
175
|
} | null;
|
|
139
176
|
[key: string]: unknown;
|
|
140
177
|
}
|
|
141
|
-
/**
|
|
142
|
-
* The snapshot the plugin flushes to disk from a dedicated writer thread. It
|
|
143
|
-
* keeps updating while the game thread is inside a modal loop or a slow task,
|
|
144
|
-
* which is precisely when a bridge request cannot be answered.
|
|
145
|
-
*/
|
|
146
178
|
export declare function readEngineSnapshot(projectPath: string | null | undefined): EngineSnapshot | null;
|
|
147
179
|
export interface EngineState {
|
|
148
180
|
running: boolean;
|
|
@@ -150,6 +182,17 @@ export interface EngineState {
|
|
|
150
182
|
log: LogState;
|
|
151
183
|
snapshot: EngineSnapshot | null;
|
|
152
184
|
dialogs: NativeWindow[];
|
|
185
|
+
/**
|
|
186
|
+
* #965: the out-of-process probe could not run. Reported on its own rather
|
|
187
|
+
* than folded into an empty `processes`, because "I could not look" and
|
|
188
|
+
* "there is no editor" are different answers and only one of them is a
|
|
189
|
+
* reason to conclude the editor is down.
|
|
190
|
+
*/
|
|
191
|
+
processProbeFailed: boolean;
|
|
192
|
+
/** What `running` is asserted on. A bridge reply outranks the process table. */
|
|
193
|
+
runningEvidence: "bridge-snapshot" | "process-table" | "none";
|
|
194
|
+
/** Where `snapshot` came from. */
|
|
195
|
+
snapshotSource: "bridge" | "status.json" | "none";
|
|
153
196
|
/** One line an agent can act on without reading any of the above. */
|
|
154
197
|
summary: string;
|
|
155
198
|
/** True when something is waiting on a human answer. */
|
|
@@ -163,3 +206,14 @@ export interface EngineState {
|
|
|
163
206
|
export declare function readEngineState(projectPath: string | null | undefined, opts?: {
|
|
164
207
|
probeWindows?: boolean;
|
|
165
208
|
}): Promise<EngineState>;
|
|
209
|
+
/**
|
|
210
|
+
* Fold in a snapshot the editor served over its own bridge.
|
|
211
|
+
*
|
|
212
|
+
* #965: one report contained `"running": false, "processes": []` and, in the
|
|
213
|
+
* same object, a live snapshot with an uptime and a ticking game thread, served
|
|
214
|
+
* BY the editor over the bridge. An editor that answers a request is running;
|
|
215
|
+
* a process table that came back empty is a failed measurement, not a fact
|
|
216
|
+
* about the world. So a bridge reply sets `running` and the process probe's
|
|
217
|
+
* silence is reported as its own field.
|
|
218
|
+
*/
|
|
219
|
+
export declare function withBridgeSnapshot(state: EngineState, snapshot: EngineSnapshot): EngineState;
|
package/dist/engine-observer.js
CHANGED
|
@@ -44,11 +44,70 @@ function parseJsonLoose(raw) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
const HEADLESS_FLAGS = ["-server", "-game", "-nullrhi", "-unattended", "-run=", "-buildmachine"];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Where a path argument can begin on a command line: the start of the string,
|
|
49
|
+
* or straight after a separator that cannot itself be part of a path (a space,
|
|
50
|
+
* a quote, or the `=` of `-Project=`). Matching the root marker rather than the
|
|
51
|
+
* whole token is what lets an unquoted path containing spaces still be found.
|
|
52
|
+
*/
|
|
53
|
+
const PATH_ROOT_RE = /(?:^|[\s"'=])((?:[A-Za-z]:[\\/])|\/)/g;
|
|
54
|
+
/**
|
|
55
|
+
* The .uproject argument on a process command line, absolute and resolved.
|
|
56
|
+
*
|
|
57
|
+
* #967/#970/#965: this used to be one lazy regex over the whole command line,
|
|
58
|
+
* `[A-Za-z]:[\\/][^"]*?\.uproject`. `[^"]` matches spaces, so on the unquoted
|
|
59
|
+
* command line Windows reports for a spawned editor
|
|
60
|
+
*
|
|
61
|
+
* C:\...\UnrealEditor.exe C:\work\Demo\Demo.uproject
|
|
62
|
+
*
|
|
63
|
+
* the match started at the drive letter of the EXECUTABLE and ran all the way
|
|
64
|
+
* to the end, and every process came back with a "project path" that was the
|
|
65
|
+
* exe and the project concatenated. Nothing ever equalled that, so
|
|
66
|
+
* editorOwnsProject was false for the very editor that had the project open:
|
|
67
|
+
* stop_editor refused to stop a healthy editor while printing that same
|
|
68
|
+
* concatenation back as its evidence, and get_engine_state reported no process
|
|
69
|
+
* for a project whose editor was answering on the bridge.
|
|
70
|
+
*
|
|
71
|
+
* So: find where the .uproject argument ENDS (a token boundary, not just any
|
|
72
|
+
* occurrence of the extension), then walk back to the last place a path could
|
|
73
|
+
* have begun. That handles the quoted form, the unquoted form, `-Project=` with
|
|
74
|
+
* and without quotes, and POSIX roots, without assuming a path has no spaces.
|
|
75
|
+
*/
|
|
76
|
+
export function extractProjectPath(commandLine) {
|
|
77
|
+
const lower = commandLine.toLowerCase();
|
|
78
|
+
const EXT = ".uproject";
|
|
79
|
+
let end = -1;
|
|
80
|
+
for (let at = lower.indexOf(EXT); at >= 0; at = lower.indexOf(EXT, at + EXT.length)) {
|
|
81
|
+
const after = commandLine[at + EXT.length];
|
|
82
|
+
if (after === undefined || /[\s"']/.test(after)) {
|
|
83
|
+
end = at + EXT.length;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (end < 0)
|
|
88
|
+
return null;
|
|
89
|
+
const head = commandLine.slice(0, end);
|
|
90
|
+
let start = -1;
|
|
91
|
+
PATH_ROOT_RE.lastIndex = 0;
|
|
92
|
+
for (let m = PATH_ROOT_RE.exec(head); m !== null; m = PATH_ROOT_RE.exec(head)) {
|
|
93
|
+
// m[0] may include the delimiter that proved this is a token boundary;
|
|
94
|
+
// the path itself starts where the captured root does.
|
|
95
|
+
start = m.index + m[0].length - m[1].length;
|
|
96
|
+
}
|
|
97
|
+
if (start < 0)
|
|
98
|
+
return null;
|
|
99
|
+
const raw = commandLine.slice(start, end).replace(/^["']|["']$/g, "").trim();
|
|
100
|
+
if (raw.length === 0)
|
|
50
101
|
return null;
|
|
51
|
-
|
|
102
|
+
// A candidate that still swallowed the executable is the exact failure this
|
|
103
|
+
// function exists to prevent, so never return one. It can only happen when
|
|
104
|
+
// the project argument itself is relative, and there the last whitespace
|
|
105
|
+
// delimited token is the honest answer.
|
|
106
|
+
if (/\.exe(\s|$)/i.test(raw)) {
|
|
107
|
+
const token = raw.split(/\s+/).pop();
|
|
108
|
+
return token && token.toLowerCase().endsWith(EXT) ? path.resolve(token) : null;
|
|
109
|
+
}
|
|
110
|
+
return path.resolve(raw);
|
|
52
111
|
}
|
|
53
112
|
function classify(pid, commandLine, responding, windowTitle) {
|
|
54
113
|
const lower = commandLine.toLowerCase();
|
|
@@ -89,40 +148,49 @@ let processCache = null;
|
|
|
89
148
|
* interactive editor for one project apart from a headless shard for another.
|
|
90
149
|
*/
|
|
91
150
|
export async function listEditorProcesses() {
|
|
151
|
+
return (await probeEditorProcesses()).processes;
|
|
152
|
+
}
|
|
153
|
+
async function probeEditorProcesses() {
|
|
92
154
|
if (processCache && Date.now() - processCache.at < PROCESS_CACHE_MS) {
|
|
93
|
-
return processCache.value;
|
|
155
|
+
return { processes: processCache.value, failed: processCache.failed };
|
|
94
156
|
}
|
|
95
|
-
const
|
|
96
|
-
processCache = { at: Date.now(), value };
|
|
97
|
-
return
|
|
157
|
+
const probe = await queryEditorProcesses();
|
|
158
|
+
processCache = { at: Date.now(), value: probe.processes, failed: probe.failed };
|
|
159
|
+
return probe;
|
|
98
160
|
}
|
|
99
161
|
async function queryEditorProcesses() {
|
|
100
162
|
try {
|
|
101
163
|
if (IS_WINDOWS) {
|
|
102
164
|
const raw = await powershell(WINDOWS_PROCESS_SCRIPT, 20000);
|
|
103
165
|
const rows = parseJsonLoose(raw);
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
166
|
+
return {
|
|
167
|
+
processes: rows
|
|
168
|
+
.filter((r) => typeof r?.pid === "number")
|
|
169
|
+
.map((r) => classify(r.pid, r.cmd ?? "", r.responding !== false, r.title ?? null)),
|
|
170
|
+
failed: false,
|
|
171
|
+
};
|
|
107
172
|
}
|
|
108
173
|
// POSIX: `ps` gives the full argv, which carries the .uproject and the
|
|
109
174
|
// headless flags. There is no cheap "responding" equivalent, so report
|
|
110
175
|
// true and let the log-staleness signal stand in for a wedge.
|
|
111
176
|
const { stdout } = await execFileAsync("ps", ["-eo", "pid=,args="], { timeout: 10000, maxBuffer: 4 * 1024 * 1024 });
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
177
|
+
return {
|
|
178
|
+
processes: stdout
|
|
179
|
+
.split("\n")
|
|
180
|
+
.map((line) => line.trim())
|
|
181
|
+
.filter((line) => /UnrealEditor/.test(line) && !/\bgrep\b/.test(line))
|
|
182
|
+
.map((line) => {
|
|
183
|
+
const space = line.indexOf(" ");
|
|
184
|
+
const pid = Number(line.slice(0, space));
|
|
185
|
+
return classify(pid, line.slice(space + 1), true, null);
|
|
186
|
+
})
|
|
187
|
+
.filter((p) => Number.isFinite(p.pid)),
|
|
188
|
+
failed: false,
|
|
189
|
+
};
|
|
122
190
|
}
|
|
123
191
|
catch (err) {
|
|
124
192
|
log.debug("engine-observer", "process probe failed", err);
|
|
125
|
-
return [];
|
|
193
|
+
return { processes: [], failed: true };
|
|
126
194
|
}
|
|
127
195
|
}
|
|
128
196
|
/** Two .uproject paths that name the same file, whatever their spelling. */
|
|
@@ -223,6 +291,56 @@ function readTailBytes(file, bytes) {
|
|
|
223
291
|
* before the plugin module exists, which is where the "editor launched but the
|
|
224
292
|
* bridge never came up" reports have always come from.
|
|
225
293
|
*/
|
|
294
|
+
/**
|
|
295
|
+
* The engine root the project was last actually opened with, from its own log.
|
|
296
|
+
*
|
|
297
|
+
* Unreal writes `LogInit: Base Directory: <engine>/Engine/Binaries/<Platform>/`
|
|
298
|
+
* in the first few lines of every run, which is the only durable record of
|
|
299
|
+
* which engine tree launched an editor. It survives the editor exiting, so it
|
|
300
|
+
* answers "which engine is this project on" while the editor is stopped, which
|
|
301
|
+
* is exactly when a full rebuild has to be resolved (#959, #974).
|
|
302
|
+
*
|
|
303
|
+
* Returns null when the log is absent, unreadable, or has no such line.
|
|
304
|
+
*/
|
|
305
|
+
export function readEngineRootFromLog(projectPath) {
|
|
306
|
+
if (!projectPath)
|
|
307
|
+
return null;
|
|
308
|
+
const file = projectLogPath(projectPath);
|
|
309
|
+
let head;
|
|
310
|
+
try {
|
|
311
|
+
const size = fs.statSync(file).size;
|
|
312
|
+
const length = Math.min(size, 64 * 1024);
|
|
313
|
+
if (length <= 0)
|
|
314
|
+
return null;
|
|
315
|
+
const buf = Buffer.alloc(length);
|
|
316
|
+
const fd = fs.openSync(file, "r");
|
|
317
|
+
try {
|
|
318
|
+
fs.readSync(fd, buf, 0, length, 0);
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
fs.closeSync(fd);
|
|
322
|
+
}
|
|
323
|
+
head = buf.toString("utf-8");
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
const match = head.match(/Log\w*:\s*(?:Base|Engine) Directory:\s*(.+)/i);
|
|
329
|
+
if (!match)
|
|
330
|
+
return null;
|
|
331
|
+
const directory = match[1].trim().replace(/[\\/]+$/, "");
|
|
332
|
+
if (!directory)
|
|
333
|
+
return null;
|
|
334
|
+
// Base Directory points inside Engine/Binaries/<Platform>; Engine Directory
|
|
335
|
+
// points at the Engine folder itself. Both sit under the root we want.
|
|
336
|
+
let current = path.resolve(directory);
|
|
337
|
+
while (current && current !== path.dirname(current)) {
|
|
338
|
+
if (path.basename(current).toLowerCase() === "engine")
|
|
339
|
+
return path.dirname(current);
|
|
340
|
+
current = path.dirname(current);
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
226
344
|
export function readLogState(projectPath, tailLines = 25) {
|
|
227
345
|
const empty = {
|
|
228
346
|
logPath: null,
|
|
@@ -386,10 +504,7 @@ export function dialogLikeWindows(windows) {
|
|
|
386
504
|
* keeps updating while the game thread is inside a modal loop or a slow task,
|
|
387
505
|
* which is precisely when a bridge request cannot be answered.
|
|
388
506
|
*/
|
|
389
|
-
|
|
390
|
-
if (!projectPath)
|
|
391
|
-
return null;
|
|
392
|
-
const file = path.join(path.dirname(projectPath), "Saved", "UE_MCP_Bridge", "status.json");
|
|
507
|
+
function readSnapshotFile(file) {
|
|
393
508
|
// The writer publishes atomically (temp file + move) four times a second, so
|
|
394
509
|
// a read can land in the instant between unlink and rename. One retry turns
|
|
395
510
|
// that transient miss back into a hit; without it, callers see a null and
|
|
@@ -406,10 +521,79 @@ export function readEngineSnapshot(projectPath) {
|
|
|
406
521
|
}
|
|
407
522
|
return null;
|
|
408
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* `status.<pid>.json` names, newest last-written first.
|
|
526
|
+
*
|
|
527
|
+
* #990: one project directory can hold the status of several processes, and a
|
|
528
|
+
* single shared `status.json` describes only whichever of them wrote last. The
|
|
529
|
+
* per-process files are the ones that describe each editor honestly. A file
|
|
530
|
+
* whose process has died stops being updated, so newest-wins picks a live
|
|
531
|
+
* editor over a crashed one without a process-table query.
|
|
532
|
+
*/
|
|
533
|
+
function instanceStatusFiles(dir) {
|
|
534
|
+
try {
|
|
535
|
+
return fs
|
|
536
|
+
.readdirSync(dir)
|
|
537
|
+
.filter((name) => /^status\.\d+\.json$/i.test(name))
|
|
538
|
+
.map((name) => {
|
|
539
|
+
const file = path.join(dir, name);
|
|
540
|
+
let mtimeMs = 0;
|
|
541
|
+
try {
|
|
542
|
+
mtimeMs = fs.statSync(file).mtimeMs;
|
|
543
|
+
}
|
|
544
|
+
catch {
|
|
545
|
+
// Removed between readdir and stat; sorts last and reads as null.
|
|
546
|
+
}
|
|
547
|
+
return { file, mtimeMs };
|
|
548
|
+
})
|
|
549
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs)
|
|
550
|
+
.map((entry) => entry.file);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
export function readEngineSnapshot(projectPath) {
|
|
557
|
+
if (!projectPath)
|
|
558
|
+
return null;
|
|
559
|
+
const dir = path.join(path.dirname(projectPath), "Saved", "UE_MCP_Bridge");
|
|
560
|
+
// Per-process first, shared second. The shared path is still written by
|
|
561
|
+
// current plugin builds and is the only thing an older one writes, so it
|
|
562
|
+
// stays the fallback rather than being dropped.
|
|
563
|
+
for (const file of instanceStatusFiles(dir)) {
|
|
564
|
+
const snapshot = readSnapshotFile(file);
|
|
565
|
+
if (snapshot)
|
|
566
|
+
return snapshot;
|
|
567
|
+
}
|
|
568
|
+
return readSnapshotFile(path.join(dir, "status.json"));
|
|
569
|
+
}
|
|
409
570
|
function summarise(state) {
|
|
410
571
|
const ours = state.processes;
|
|
572
|
+
if (ours.length === 0 && !state.running) {
|
|
573
|
+
return {
|
|
574
|
+
summary: state.processProbeFailed
|
|
575
|
+
? `The process probe failed, so whether an editor is running for this project is unknown. Log phase: ${state.log.phase}.`
|
|
576
|
+
: `No editor process for this project. Log phase: ${state.log.phase}.`,
|
|
577
|
+
blocked: false,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
// #965: an editor answered this request over the bridge while the process
|
|
581
|
+
// table came back empty, and the report said "No editor process for this
|
|
582
|
+
// project" in the same breath as it returned that editor's own snapshot.
|
|
583
|
+
// Whatever the process table managed to see, an editor that replied exists.
|
|
411
584
|
if (ours.length === 0) {
|
|
412
|
-
|
|
585
|
+
const modal = state.snapshot?.modal;
|
|
586
|
+
if (modal) {
|
|
587
|
+
return {
|
|
588
|
+
summary: `Editor answered over the bridge and is blocked on modal "${modal.title}": ${modal.message} [${(modal.buttons ?? []).join(", ")}]`,
|
|
589
|
+
blocked: true,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
summary: `Editor answered over the bridge (phase: ${state.snapshot?.phase ?? state.log.phase}), but the process probe ` +
|
|
594
|
+
`${state.processProbeFailed ? "failed" : "did not find it"}, so no pid or command line is available.`,
|
|
595
|
+
blocked: false,
|
|
596
|
+
};
|
|
413
597
|
}
|
|
414
598
|
if (state.dialogs.length > 0) {
|
|
415
599
|
const d = state.dialogs[0];
|
|
@@ -452,14 +636,50 @@ function summarise(state) {
|
|
|
452
636
|
* be turned on when something already looks wrong.
|
|
453
637
|
*/
|
|
454
638
|
export async function readEngineState(projectPath, opts = {}) {
|
|
455
|
-
const
|
|
639
|
+
const probe = await probeEditorProcesses();
|
|
640
|
+
// Every interactive editor holding this .uproject open, not just one: two
|
|
641
|
+
// editors on one project is a legitimate state and hiding the second is how
|
|
642
|
+
// a stop lands on the wrong one (#965).
|
|
643
|
+
const processes = selectEditorsForProject(probe.processes, projectPath);
|
|
456
644
|
const logState = readLogState(projectPath);
|
|
457
645
|
const snapshot = readEngineSnapshot(projectPath);
|
|
458
646
|
let dialogs = [];
|
|
459
647
|
if (opts.probeWindows && processes.length > 0) {
|
|
460
648
|
dialogs = dialogLikeWindows(await readNativeWindows(processes.map((p) => p.pid)));
|
|
461
649
|
}
|
|
462
|
-
const base = {
|
|
650
|
+
const base = {
|
|
651
|
+
running: processes.length > 0,
|
|
652
|
+
processes,
|
|
653
|
+
log: logState,
|
|
654
|
+
snapshot,
|
|
655
|
+
dialogs,
|
|
656
|
+
processProbeFailed: probe.failed,
|
|
657
|
+
runningEvidence: processes.length > 0 ? "process-table" : "none",
|
|
658
|
+
snapshotSource: snapshot ? "status.json" : "none",
|
|
659
|
+
};
|
|
660
|
+
return { ...base, ...summarise(base) };
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Fold in a snapshot the editor served over its own bridge.
|
|
664
|
+
*
|
|
665
|
+
* #965: one report contained `"running": false, "processes": []` and, in the
|
|
666
|
+
* same object, a live snapshot with an uptime and a ticking game thread, served
|
|
667
|
+
* BY the editor over the bridge. An editor that answers a request is running;
|
|
668
|
+
* a process table that came back empty is a failed measurement, not a fact
|
|
669
|
+
* about the world. So a bridge reply sets `running` and the process probe's
|
|
670
|
+
* silence is reported as its own field.
|
|
671
|
+
*/
|
|
672
|
+
export function withBridgeSnapshot(state, snapshot) {
|
|
673
|
+
const base = {
|
|
674
|
+
...state,
|
|
675
|
+
// It was served in answer to this call, so its age is zero. Leaving it
|
|
676
|
+
// unset made the summary's freshness gate treat it as 999 seconds old and
|
|
677
|
+
// skip the modal and slow-task lines it was fetched to provide.
|
|
678
|
+
snapshot: { ...snapshot, ageSeconds: 0 },
|
|
679
|
+
snapshotSource: "bridge",
|
|
680
|
+
running: true,
|
|
681
|
+
runningEvidence: "bridge-snapshot",
|
|
682
|
+
};
|
|
463
683
|
return { ...base, ...summarise(base) };
|
|
464
684
|
}
|
|
465
685
|
//# sourceMappingURL=engine-observer.js.map
|