ue-mcp 1.2.4 → 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.
Files changed (243) hide show
  1. package/README.md +4 -3
  2. package/dist/action-class.js +56 -3
  3. package/dist/action-class.js.map +1 -1
  4. package/dist/bridge-timeouts.d.ts +64 -0
  5. package/dist/bridge-timeouts.js +118 -0
  6. package/dist/bridge-timeouts.js.map +1 -0
  7. package/dist/bridge.js +10 -2
  8. package/dist/bridge.js.map +1 -1
  9. package/dist/editor-control.d.ts +72 -5
  10. package/dist/editor-control.js +164 -140
  11. package/dist/editor-control.js.map +1 -1
  12. package/dist/editor-target.d.ts +55 -1
  13. package/dist/editor-target.js +107 -10
  14. package/dist/editor-target.js.map +1 -1
  15. package/dist/engine-observer.d.ts +59 -5
  16. package/dist/engine-observer.js +249 -29
  17. package/dist/engine-observer.js.map +1 -1
  18. package/dist/engine-root.d.ts +91 -0
  19. package/dist/engine-root.js +448 -0
  20. package/dist/engine-root.js.map +1 -0
  21. package/dist/epic-enrich.js +16 -1
  22. package/dist/epic-enrich.js.map +1 -1
  23. package/dist/feedback-cli.js +4 -0
  24. package/dist/feedback-cli.js.map +1 -1
  25. package/dist/feedback-deferred.d.ts +9 -0
  26. package/dist/feedback-deferred.js +2 -0
  27. package/dist/feedback-deferred.js.map +1 -1
  28. package/dist/feedback-fallback.d.ts +99 -0
  29. package/dist/feedback-fallback.js +173 -0
  30. package/dist/feedback-fallback.js.map +1 -0
  31. package/dist/flow/write-methods.js +20 -0
  32. package/dist/flow/write-methods.js.map +1 -1
  33. package/dist/lean-context.js +8 -3
  34. package/dist/lean-context.js.map +1 -1
  35. package/dist/locking.js +11 -2
  36. package/dist/locking.js.map +1 -1
  37. package/dist/project.d.ts +11 -0
  38. package/dist/project.js +23 -2
  39. package/dist/project.js.map +1 -1
  40. package/dist/python-gate.d.ts +95 -0
  41. package/dist/python-gate.js +175 -0
  42. package/dist/python-gate.js.map +1 -0
  43. package/dist/tool-counts.json +16 -16
  44. package/dist/tools/animation.js +288 -24
  45. package/dist/tools/animation.js.map +1 -1
  46. package/dist/tools/asset.js +79 -14
  47. package/dist/tools/asset.js.map +1 -1
  48. package/dist/tools/audio.js +59 -5
  49. package/dist/tools/audio.js.map +1 -1
  50. package/dist/tools/blueprint.js +29 -12
  51. package/dist/tools/blueprint.js.map +1 -1
  52. package/dist/tools/chooser.js +4 -1
  53. package/dist/tools/chooser.js.map +1 -1
  54. package/dist/tools/editor.js +81 -43
  55. package/dist/tools/editor.js.map +1 -1
  56. package/dist/tools/feedback.js +214 -48
  57. package/dist/tools/feedback.js.map +1 -1
  58. package/dist/tools/foliage.js +27 -1
  59. package/dist/tools/foliage.js.map +1 -1
  60. package/dist/tools/gameplay.js +38 -11
  61. package/dist/tools/gameplay.js.map +1 -1
  62. package/dist/tools/gas.js +17 -5
  63. package/dist/tools/gas.js.map +1 -1
  64. package/dist/tools/landscape.js +22 -7
  65. package/dist/tools/landscape.js.map +1 -1
  66. package/dist/tools/level.js +322 -51
  67. package/dist/tools/level.js.map +1 -1
  68. package/dist/tools/material.js +10 -4
  69. package/dist/tools/material.js.map +1 -1
  70. package/dist/tools/niagara.js +3 -2
  71. package/dist/tools/niagara.js.map +1 -1
  72. package/dist/tools/pcg.js +6 -5
  73. package/dist/tools/pcg.js.map +1 -1
  74. package/dist/tools/project.js +47 -20
  75. package/dist/tools/project.js.map +1 -1
  76. package/dist/types.d.ts +21 -0
  77. package/dist/types.js +46 -3
  78. package/dist/types.js.map +1 -1
  79. package/dist/ue-mcp.default.yml +493 -125
  80. package/package.json +2 -2
  81. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  82. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  83. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  84. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  85. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  86. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  87. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  88. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  89. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  90. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  91. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  92. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  93. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  94. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  95. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  242. package/skills/ue-mcp-animation/SKILL.md +154 -0
  243. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -0,0 +1,757 @@
1
+ // Bounded visibility control for live PIE actors and scene components.
2
+ //
3
+ // Raw reflected property writes do not call the semantic visibility setters,
4
+ // and invoking one function at a time cannot safely preflight or roll back a
5
+ // filtered batch. These handlers resolve the complete target set first, cap it,
6
+ // capture exact prior state, and never save or dirty editor packages.
7
+
8
+ #include "EditorHandlers.h"
9
+
10
+ #include "HandlerUtils.h"
11
+
12
+ #include "Components/SceneComponent.h"
13
+ #include "Engine/Engine.h"
14
+ #include "Engine/World.h"
15
+ #include "EngineUtils.h"
16
+ #include "GameFramework/Actor.h"
17
+ #include "Misc/Guid.h"
18
+
19
+ namespace
20
+ {
21
+ constexpr int32 MCPRuntimeVisibilityDefaultMaxTargets = 64;
22
+ constexpr int32 MCPRuntimeVisibilityAbsoluteMaxTargets = 256;
23
+ constexpr int32 MCPRuntimeVisibilityMaxExplicitActors = 64;
24
+ constexpr int32 MCPRuntimeVisibilityMaxComponentFilters = 64;
25
+ constexpr int32 MCPRuntimeVisibilityMaxRollbackSnapshots = 64;
26
+
27
+ struct FMCPRuntimeVisibilityTarget
28
+ {
29
+ AActor* Actor = nullptr;
30
+ USceneComponent* Component = nullptr;
31
+ bool bDirectComponentMatch = false;
32
+ bool bPreviousActorHidden = false;
33
+ bool bPreviousVisible = true;
34
+ bool bPreviousComponentHidden = false;
35
+ };
36
+
37
+ struct FMCPRuntimeVisibilityRestoreTarget
38
+ {
39
+ TWeakObjectPtr<AActor> Actor;
40
+ TWeakObjectPtr<USceneComponent> Component;
41
+ bool bActorHidden = false;
42
+ bool bVisible = true;
43
+ bool bComponentHidden = false;
44
+ };
45
+
46
+ struct FMCPRuntimeVisibilityRollbackSnapshot
47
+ {
48
+ TWeakObjectPtr<UWorld> World;
49
+ int32 PIEInstance = INDEX_NONE;
50
+ uint64 Sequence = 0;
51
+ TArray<FMCPRuntimeVisibilityRestoreTarget> Targets;
52
+ };
53
+
54
+ TMap<FString, FMCPRuntimeVisibilityRollbackSnapshot> GMCPRuntimeVisibilityRollbacks;
55
+ uint64 GMCPRuntimeVisibilityRollbackSequence = 0;
56
+
57
+ bool ReadMCPRuntimeVisibilityStringArray(
58
+ const TSharedPtr<FJsonObject>& Params,
59
+ const TCHAR* Field,
60
+ int32 MaxValues,
61
+ TArray<FString>& OutValues,
62
+ FString& OutError)
63
+ {
64
+ if (!Params->HasField(Field)) return true;
65
+
66
+ const TArray<TSharedPtr<FJsonValue>>* Values = nullptr;
67
+ if (!Params->TryGetArrayField(Field, Values) || !Values)
68
+ {
69
+ OutError = FString::Printf(TEXT("'%s' must be an array of strings"), Field);
70
+ return false;
71
+ }
72
+ if (Values->Num() == 0)
73
+ {
74
+ OutError = FString::Printf(TEXT("'%s' must not be empty"), Field);
75
+ return false;
76
+ }
77
+ if (Values->Num() > MaxValues)
78
+ {
79
+ OutError = FString::Printf(TEXT("'%s' accepts at most %d values"), Field, MaxValues);
80
+ return false;
81
+ }
82
+
83
+ TSet<FString> Seen;
84
+ for (const TSharedPtr<FJsonValue>& Value : *Values)
85
+ {
86
+ FString Text;
87
+ if (!Value.IsValid() || !Value->TryGetString(Text) || Text.TrimStartAndEnd().IsEmpty())
88
+ {
89
+ OutError = FString::Printf(TEXT("'%s' contains a non-string or empty value"), Field);
90
+ return false;
91
+ }
92
+ Text = Text.TrimStartAndEnd();
93
+ const FString Key = Text.ToLower();
94
+ if (!Seen.Contains(Key))
95
+ {
96
+ Seen.Add(Key);
97
+ OutValues.Add(Text);
98
+ }
99
+ }
100
+ return true;
101
+ }
102
+
103
+ bool MCPRuntimeVisibilityClassMatches(UClass* Candidate, UClass* Requested, bool bMatchSubclasses)
104
+ {
105
+ return Candidate && Requested &&
106
+ (bMatchSubclasses ? Candidate->IsChildOf(Requested) : Candidate == Requested);
107
+ }
108
+
109
+ bool MCPRuntimeVisibilityMatchesAnyClass(
110
+ UClass* Candidate,
111
+ const TArray<UClass*>& Requested,
112
+ bool bMatchSubclasses)
113
+ {
114
+ if (Requested.Num() == 0) return true;
115
+ for (UClass* RequestedClass : Requested)
116
+ {
117
+ if (MCPRuntimeVisibilityClassMatches(Candidate, RequestedClass, bMatchSubclasses)) return true;
118
+ }
119
+ return false;
120
+ }
121
+
122
+ bool MCPRuntimeVisibilityMatchesComponent(
123
+ USceneComponent* Component,
124
+ const TArray<FString>& ComponentNames,
125
+ const TArray<UClass*>& ComponentClasses,
126
+ bool bMatchSubclasses)
127
+ {
128
+ if (!Component) return false;
129
+ if (ComponentNames.Num() == 0 && ComponentClasses.Num() == 0) return true;
130
+
131
+ for (const FString& Name : ComponentNames)
132
+ {
133
+ if (Component->GetName().Equals(Name, ESearchCase::IgnoreCase)) return true;
134
+ }
135
+ return ComponentClasses.Num() > 0 &&
136
+ MCPRuntimeVisibilityMatchesAnyClass(Component->GetClass(), ComponentClasses, bMatchSubclasses);
137
+ }
138
+
139
+ bool MCPRuntimeVisibilityTryAddComponent(
140
+ TMap<USceneComponent*, bool>& ComponentTargets,
141
+ USceneComponent* Component,
142
+ bool bDirectMatch,
143
+ int32 ActorTargetCount,
144
+ int32 MaxTargets)
145
+ {
146
+ if (!Component) return true;
147
+ bool* Existing = ComponentTargets.Find(Component);
148
+ if (Existing)
149
+ {
150
+ *Existing = *Existing || bDirectMatch;
151
+ return true;
152
+ }
153
+ if (ActorTargetCount + ComponentTargets.Num() >= MaxTargets) return false;
154
+ ComponentTargets.Add(Component, bDirectMatch);
155
+ return true;
156
+ }
157
+
158
+ bool MCPRuntimeVisibilityAddComponentTree(
159
+ TMap<USceneComponent*, bool>& ComponentTargets,
160
+ USceneComponent* Root,
161
+ int32 ActorTargetCount,
162
+ int32 MaxTargets)
163
+ {
164
+ TArray<USceneComponent*> Pending;
165
+ Pending.Add(Root);
166
+ while (Pending.Num() > 0)
167
+ {
168
+ USceneComponent* Parent = Pending.Pop(EAllowShrinking::No);
169
+ for (USceneComponent* Child : Parent->GetAttachChildren())
170
+ {
171
+ if (!Child || ComponentTargets.Contains(Child)) continue;
172
+ if (!MCPRuntimeVisibilityTryAddComponent(
173
+ ComponentTargets, Child, false, ActorTargetCount, MaxTargets))
174
+ {
175
+ return false;
176
+ }
177
+ Pending.Add(Child);
178
+ }
179
+ }
180
+ return true;
181
+ }
182
+
183
+ void MCPRuntimeVisibilityRemoveExpiredRollbacks()
184
+ {
185
+ for (auto It = GMCPRuntimeVisibilityRollbacks.CreateIterator(); It; ++It)
186
+ {
187
+ if (!It.Value().World.IsValid()) It.RemoveCurrent();
188
+ }
189
+ }
190
+
191
+ void MCPRuntimeVisibilityMakeRollbackRoom()
192
+ {
193
+ MCPRuntimeVisibilityRemoveExpiredRollbacks();
194
+ while (GMCPRuntimeVisibilityRollbacks.Num() >= MCPRuntimeVisibilityMaxRollbackSnapshots)
195
+ {
196
+ FString OldestToken;
197
+ uint64 OldestSequence = MAX_uint64;
198
+ for (const TPair<FString, FMCPRuntimeVisibilityRollbackSnapshot>& Pair :
199
+ GMCPRuntimeVisibilityRollbacks)
200
+ {
201
+ if (Pair.Value.Sequence < OldestSequence)
202
+ {
203
+ OldestSequence = Pair.Value.Sequence;
204
+ OldestToken = Pair.Key;
205
+ }
206
+ }
207
+ if (OldestToken.IsEmpty()) break;
208
+ GMCPRuntimeVisibilityRollbacks.Remove(OldestToken);
209
+ }
210
+ }
211
+
212
+ FString MCPRuntimeVisibilityStoreRollback(
213
+ UWorld* World,
214
+ int32 PIEInstance,
215
+ const TArray<FMCPRuntimeVisibilityRestoreTarget>& Targets)
216
+ {
217
+ MCPRuntimeVisibilityMakeRollbackRoom();
218
+ const FString Token = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphensLower);
219
+ FMCPRuntimeVisibilityRollbackSnapshot& Snapshot =
220
+ GMCPRuntimeVisibilityRollbacks.Add(Token);
221
+ Snapshot.World = World;
222
+ Snapshot.PIEInstance = PIEInstance;
223
+ Snapshot.Sequence = ++GMCPRuntimeVisibilityRollbackSequence;
224
+ Snapshot.Targets = Targets;
225
+ return Token;
226
+ }
227
+
228
+ TSharedPtr<FJsonObject> MCPRuntimeVisibilityActorResult(
229
+ const FMCPRuntimeVisibilityTarget& Target,
230
+ bool bHidden)
231
+ {
232
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
233
+ Entry->SetStringField(TEXT("kind"), TEXT("actor"));
234
+ Entry->SetStringField(TEXT("actorLabel"), Target.Actor->GetActorLabel());
235
+ Entry->SetStringField(TEXT("actorPath"), Target.Actor->GetPathName());
236
+ Entry->SetStringField(TEXT("actorClass"), Target.Actor->GetClass()->GetPathName());
237
+ Entry->SetBoolField(TEXT("previousHidden"), Target.bPreviousActorHidden);
238
+ Entry->SetBoolField(TEXT("desiredHidden"), bHidden);
239
+ Entry->SetBoolField(TEXT("changed"), Target.bPreviousActorHidden != bHidden);
240
+ return Entry;
241
+ }
242
+
243
+ TSharedPtr<FJsonObject> MCPRuntimeVisibilityComponentResult(
244
+ const FMCPRuntimeVisibilityTarget& Target,
245
+ bool bHidden)
246
+ {
247
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
248
+ Entry->SetStringField(TEXT("kind"), TEXT("component"));
249
+ Entry->SetStringField(TEXT("actorLabel"), Target.Actor->GetActorLabel());
250
+ Entry->SetStringField(TEXT("actorPath"), Target.Actor->GetPathName());
251
+ Entry->SetStringField(TEXT("componentName"), Target.Component->GetName());
252
+ Entry->SetStringField(TEXT("componentPath"), Target.Component->GetPathName());
253
+ Entry->SetStringField(TEXT("componentClass"), Target.Component->GetClass()->GetPathName());
254
+ Entry->SetBoolField(TEXT("directMatch"), Target.bDirectComponentMatch);
255
+ Entry->SetBoolField(TEXT("previousVisible"), Target.bPreviousVisible);
256
+ Entry->SetBoolField(TEXT("previousHiddenInGame"), Target.bPreviousComponentHidden);
257
+ Entry->SetBoolField(TEXT("desiredVisible"), !bHidden);
258
+ Entry->SetBoolField(TEXT("desiredHiddenInGame"), bHidden);
259
+ Entry->SetBoolField(TEXT("changed"),
260
+ Target.bPreviousVisible != !bHidden || Target.bPreviousComponentHidden != bHidden);
261
+ return Entry;
262
+ }
263
+
264
+ FMCPRuntimeVisibilityRestoreTarget MCPRuntimeVisibilityRestoreActor(
265
+ const FMCPRuntimeVisibilityTarget& Target)
266
+ {
267
+ FMCPRuntimeVisibilityRestoreTarget Entry;
268
+ Entry.Actor = Target.Actor;
269
+ Entry.bActorHidden = Target.bPreviousActorHidden;
270
+ return Entry;
271
+ }
272
+
273
+ FMCPRuntimeVisibilityRestoreTarget MCPRuntimeVisibilityRestoreComponent(
274
+ const FMCPRuntimeVisibilityTarget& Target)
275
+ {
276
+ FMCPRuntimeVisibilityRestoreTarget Entry;
277
+ Entry.Component = Target.Component;
278
+ Entry.bVisible = Target.bPreviousVisible;
279
+ Entry.bComponentHidden = Target.bPreviousComponentHidden;
280
+ return Entry;
281
+ }
282
+
283
+ TSharedPtr<FJsonValue> MCPRuntimeVisibilityRequirePIEWorld(
284
+ const TSharedPtr<FJsonObject>& Params,
285
+ UWorld*& OutWorld,
286
+ const FWorldContext*& OutContext)
287
+ {
288
+ FString WorldScope = TEXT("pie");
289
+ if (Params->HasField(TEXT("world")) &&
290
+ (!Params->TryGetStringField(TEXT("world"), WorldScope) || WorldScope.IsEmpty()))
291
+ {
292
+ return MCPError(TEXT("'world' must be the string 'pie'"));
293
+ }
294
+ WorldScope = WorldScope.ToLower();
295
+ if (WorldScope != TEXT("pie"))
296
+ {
297
+ return MCPError(TEXT("Runtime visibility is PIE-only; 'world' must be 'pie'"));
298
+ }
299
+
300
+ double RawPIEInstance = INDEX_NONE;
301
+ const bool bHasPIEInstance = Params->HasField(TEXT("pieInstance"));
302
+ if (bHasPIEInstance && !Params->TryGetNumberField(TEXT("pieInstance"), RawPIEInstance))
303
+ {
304
+ return MCPError(TEXT("'pieInstance' must be an integer"));
305
+ }
306
+ const int32 PIEInstance = bHasPIEInstance ? FMath::RoundToInt(RawPIEInstance) : INDEX_NONE;
307
+ if (bHasPIEInstance && !FMath::IsNearlyEqual(RawPIEInstance, PIEInstance))
308
+ {
309
+ return MCPError(TEXT("'pieInstance' must be an integer"));
310
+ }
311
+
312
+ OutWorld = nullptr;
313
+ OutContext = nullptr;
314
+ int32 AvailablePIEWorlds = 0;
315
+ if (GEngine)
316
+ {
317
+ for (const FWorldContext& Context : GEngine->GetWorldContexts())
318
+ {
319
+ if (Context.WorldType != EWorldType::PIE || !Context.World()) continue;
320
+ ++AvailablePIEWorlds;
321
+ if (bHasPIEInstance && Context.PIEInstance != PIEInstance) continue;
322
+ if (!OutWorld)
323
+ {
324
+ OutWorld = Context.World();
325
+ OutContext = &Context;
326
+ }
327
+ }
328
+ }
329
+ if (!bHasPIEInstance && AvailablePIEWorlds > 1)
330
+ {
331
+ OutWorld = nullptr;
332
+ OutContext = nullptr;
333
+ return MCPError(TEXT("Multiple PIE worlds are active; provide an explicit integer 'pieInstance'"));
334
+ }
335
+ if (!OutWorld || !OutContext)
336
+ {
337
+ return MCPError(TEXT("PIE world not available (or no such pieInstance). See editor(list_pie_instances)."));
338
+ }
339
+ return nullptr;
340
+ }
341
+ }
342
+
343
+ TSharedPtr<FJsonValue> FEditorHandlers::SetRuntimeVisibility(const TSharedPtr<FJsonObject>& Params)
344
+ {
345
+ MCP_CHECK_GAME_THREAD();
346
+ if (!Params.IsValid()) return MCPError(TEXT("Missing params"));
347
+
348
+ bool bHidden = false;
349
+ if (!Params->TryGetBoolField(TEXT("hidden"), bHidden))
350
+ {
351
+ return MCPError(TEXT("Missing required boolean 'hidden'"));
352
+ }
353
+ if (Params->HasField(TEXT("all")))
354
+ {
355
+ return MCPError(TEXT("'all' is not supported; use explicit actorLabels/actorPaths or actorClass"));
356
+ }
357
+
358
+ const int32 MaxTargets = OptionalInt(
359
+ Params, TEXT("maxTargets"), MCPRuntimeVisibilityDefaultMaxTargets);
360
+ if (MaxTargets < 1 || MaxTargets > MCPRuntimeVisibilityAbsoluteMaxTargets)
361
+ {
362
+ return MCPError(FString::Printf(
363
+ TEXT("'maxTargets' must be between 1 and %d"),
364
+ MCPRuntimeVisibilityAbsoluteMaxTargets));
365
+ }
366
+
367
+ TArray<FString> ActorLabels;
368
+ TArray<FString> ActorPaths;
369
+ TArray<FString> ComponentNames;
370
+ TArray<FString> ComponentClasses;
371
+ FString ParseError;
372
+ if (!ReadMCPRuntimeVisibilityStringArray(
373
+ Params, TEXT("actorLabels"), MCPRuntimeVisibilityMaxExplicitActors, ActorLabels, ParseError) ||
374
+ !ReadMCPRuntimeVisibilityStringArray(
375
+ Params, TEXT("actorPaths"), MCPRuntimeVisibilityMaxExplicitActors, ActorPaths, ParseError) ||
376
+ !ReadMCPRuntimeVisibilityStringArray(
377
+ Params, TEXT("componentNames"), MCPRuntimeVisibilityMaxComponentFilters, ComponentNames, ParseError) ||
378
+ !ReadMCPRuntimeVisibilityStringArray(
379
+ Params, TEXT("componentClasses"), MCPRuntimeVisibilityMaxComponentFilters, ComponentClasses, ParseError))
380
+ {
381
+ return MCPError(ParseError);
382
+ }
383
+
384
+ FString ActorClass;
385
+ const bool bHasActorClass = Params->HasField(TEXT("actorClass"));
386
+ if (bHasActorClass &&
387
+ (!Params->TryGetStringField(TEXT("actorClass"), ActorClass) || ActorClass.TrimStartAndEnd().IsEmpty()))
388
+ {
389
+ return MCPError(TEXT("'actorClass' must be a non-empty string"));
390
+ }
391
+ ActorClass = ActorClass.TrimStartAndEnd();
392
+
393
+ const int32 SelectorCount =
394
+ (Params->HasField(TEXT("actorLabels")) ? 1 : 0) +
395
+ (Params->HasField(TEXT("actorPaths")) ? 1 : 0) +
396
+ (bHasActorClass ? 1 : 0);
397
+ if (SelectorCount != 1)
398
+ {
399
+ return MCPError(TEXT("Provide exactly one actor selector: actorLabels, actorPaths, or actorClass"));
400
+ }
401
+
402
+ const bool bHasComponentFilters = ComponentNames.Num() > 0 || ComponentClasses.Num() > 0;
403
+ const bool bAffectActor = OptionalBool(Params, TEXT("affectActor"), !bHasComponentFilters);
404
+ const bool bAffectComponents = OptionalBool(Params, TEXT("affectComponents"), bHasComponentFilters);
405
+ if (!bAffectActor && !bAffectComponents)
406
+ {
407
+ return MCPError(TEXT("At least one of 'affectActor' or 'affectComponents' must be true"));
408
+ }
409
+ if (bHasComponentFilters && !bAffectComponents)
410
+ {
411
+ return MCPError(TEXT("componentNames/componentClasses require 'affectComponents'=true"));
412
+ }
413
+
414
+ UClass* ResolvedActorClass = nullptr;
415
+ if (bHasActorClass)
416
+ {
417
+ ResolvedActorClass = MCPResolveClass(ActorClass);
418
+ if (TSharedPtr<FJsonValue> Error = MCPCheckClassUsable(
419
+ ActorClass, ResolvedActorClass, AActor::StaticClass(), false))
420
+ {
421
+ return Error;
422
+ }
423
+ }
424
+
425
+ TArray<UClass*> ResolvedComponentClasses;
426
+ for (const FString& ComponentClass : ComponentClasses)
427
+ {
428
+ UClass* ResolvedClass = MCPResolveClass(ComponentClass);
429
+ if (!ResolvedClass) return MCPClassNotFoundError(ComponentClass, TEXT("componentClasses"));
430
+ if (!ResolvedClass->IsChildOf(USceneComponent::StaticClass()))
431
+ {
432
+ return MCPClassUnusableError(
433
+ ComponentClass,
434
+ ResolvedClass,
435
+ TEXT("wrong_base"),
436
+ TEXT("it does not derive from SceneComponent"));
437
+ }
438
+ ResolvedComponentClasses.AddUnique(ResolvedClass);
439
+ }
440
+
441
+ UWorld* World = nullptr;
442
+ const FWorldContext* WorldContext = nullptr;
443
+ if (TSharedPtr<FJsonValue> Error = MCPRuntimeVisibilityRequirePIEWorld(
444
+ Params, World, WorldContext)) return Error;
445
+
446
+ const bool bMatchSubclasses = OptionalBool(Params, TEXT("matchSubclasses"), true);
447
+ TArray<AActor*> SelectedActors;
448
+ if (bHasActorClass)
449
+ {
450
+ for (TActorIterator<AActor> It(World); It; ++It)
451
+ {
452
+ if (MCPRuntimeVisibilityClassMatches(It->GetClass(), ResolvedActorClass, bMatchSubclasses))
453
+ {
454
+ SelectedActors.Add(*It);
455
+ if (SelectedActors.Num() > MaxTargets)
456
+ {
457
+ return MCPError(FString::Printf(
458
+ TEXT("Actor selector matched more than maxTargets=%d actors; narrow the class or raise the bound deliberately"),
459
+ MaxTargets));
460
+ }
461
+ }
462
+ }
463
+ }
464
+ else
465
+ {
466
+ const bool bByPath = ActorPaths.Num() > 0;
467
+ const TArray<FString>& Requested = bByPath ? ActorPaths : ActorLabels;
468
+ for (const FString& Selector : Requested)
469
+ {
470
+ TArray<AActor*> Matches;
471
+ for (TActorIterator<AActor> It(World); It; ++It)
472
+ {
473
+ const bool bMatches = bByPath
474
+ ? It->GetPathName().Equals(Selector, ESearchCase::IgnoreCase)
475
+ : It->GetActorLabel().Equals(Selector, ESearchCase::IgnoreCase);
476
+ if (bMatches) Matches.Add(*It);
477
+ }
478
+ if (Matches.Num() == 0)
479
+ {
480
+ return MCPError(FString::Printf(TEXT("Actor selector matched nothing: %s"), *Selector));
481
+ }
482
+ if (Matches.Num() > 1)
483
+ {
484
+ return MCPError(FString::Printf(
485
+ TEXT("Actor selector is ambiguous (%d matches): %s. Use actorPaths instead."),
486
+ Matches.Num(), *Selector));
487
+ }
488
+ SelectedActors.AddUnique(Matches[0]);
489
+ }
490
+ }
491
+
492
+ SelectedActors.Sort([](const AActor& Left, const AActor& Right)
493
+ {
494
+ return Left.GetPathName() < Right.GetPathName();
495
+ });
496
+
497
+ TMap<USceneComponent*, bool> ComponentTargets;
498
+ if (bAffectComponents)
499
+ {
500
+ const int32 ActorTargetCount = bAffectActor ? SelectedActors.Num() : 0;
501
+ const bool bPropagateToChildren = OptionalBool(Params, TEXT("propagateToChildren"), true);
502
+ for (AActor* Actor : SelectedActors)
503
+ {
504
+ for (UActorComponent* OwnedComponent : Actor->GetComponents())
505
+ {
506
+ USceneComponent* Component = Cast<USceneComponent>(OwnedComponent);
507
+ if (!MCPRuntimeVisibilityMatchesComponent(
508
+ Component, ComponentNames, ResolvedComponentClasses, bMatchSubclasses))
509
+ {
510
+ continue;
511
+ }
512
+
513
+ if (!MCPRuntimeVisibilityTryAddComponent(
514
+ ComponentTargets, Component, true, ActorTargetCount, MaxTargets))
515
+ {
516
+ return MCPError(FString::Printf(
517
+ TEXT("Visibility request exceeds maxTargets=%d while expanding components"), MaxTargets));
518
+ }
519
+ if (bPropagateToChildren)
520
+ {
521
+ if (!MCPRuntimeVisibilityAddComponentTree(
522
+ ComponentTargets, Component, ActorTargetCount, MaxTargets))
523
+ {
524
+ return MCPError(FString::Printf(
525
+ TEXT("Visibility request exceeds maxTargets=%d while expanding component descendants"),
526
+ MaxTargets));
527
+ }
528
+ }
529
+ }
530
+ }
531
+ }
532
+
533
+ const int32 TargetCount = (bAffectActor ? SelectedActors.Num() : 0) + ComponentTargets.Num();
534
+ if (TargetCount > MaxTargets)
535
+ {
536
+ return MCPError(FString::Printf(
537
+ TEXT("Visibility request resolves to %d targets, above maxTargets=%d. Narrow selectors or raise the bound deliberately."),
538
+ TargetCount, MaxTargets));
539
+ }
540
+
541
+ TArray<FMCPRuntimeVisibilityTarget> Targets;
542
+ Targets.Reserve(TargetCount);
543
+ if (bAffectActor)
544
+ {
545
+ for (AActor* Actor : SelectedActors)
546
+ {
547
+ FMCPRuntimeVisibilityTarget& Target = Targets.AddDefaulted_GetRef();
548
+ Target.Actor = Actor;
549
+ Target.bPreviousActorHidden = Actor->IsHidden();
550
+ }
551
+ }
552
+
553
+ TArray<USceneComponent*> SortedComponents;
554
+ ComponentTargets.GetKeys(SortedComponents);
555
+ SortedComponents.Sort([](const USceneComponent& Left, const USceneComponent& Right)
556
+ {
557
+ return Left.GetPathName() < Right.GetPathName();
558
+ });
559
+ for (USceneComponent* Component : SortedComponents)
560
+ {
561
+ FMCPRuntimeVisibilityTarget& Target = Targets.AddDefaulted_GetRef();
562
+ Target.Actor = Component->GetOwner();
563
+ Target.Component = Component;
564
+ Target.bDirectComponentMatch = ComponentTargets.FindRef(Component);
565
+ Target.bPreviousVisible = Component->GetVisibleFlag();
566
+ Target.bPreviousComponentHidden = Component->bHiddenInGame != 0;
567
+ }
568
+
569
+ const bool bDryRun = OptionalBool(Params, TEXT("dryRun"), true);
570
+ int32 Changed = 0;
571
+ TArray<TSharedPtr<FJsonValue>> Affected;
572
+ TArray<FMCPRuntimeVisibilityRestoreTarget> RestoreTargets;
573
+ for (const FMCPRuntimeVisibilityTarget& Target : Targets)
574
+ {
575
+ const bool bTargetChanged = Target.Component
576
+ ? Target.bPreviousVisible != !bHidden || Target.bPreviousComponentHidden != bHidden
577
+ : Target.bPreviousActorHidden != bHidden;
578
+ if (bTargetChanged) ++Changed;
579
+
580
+ if (Target.Component)
581
+ {
582
+ Affected.Add(MakeShared<FJsonValueObject>(MCPRuntimeVisibilityComponentResult(Target, bHidden)));
583
+ if (bTargetChanged)
584
+ {
585
+ RestoreTargets.Add(MCPRuntimeVisibilityRestoreComponent(Target));
586
+ if (!bDryRun)
587
+ {
588
+ // Descendants are captured as independent targets, so apply without
589
+ // recursive propagation and preserve an exact rollback snapshot.
590
+ Target.Component->SetVisibility(!bHidden, false);
591
+ Target.Component->SetHiddenInGame(bHidden, false);
592
+ }
593
+ }
594
+ }
595
+ else
596
+ {
597
+ Affected.Add(MakeShared<FJsonValueObject>(MCPRuntimeVisibilityActorResult(Target, bHidden)));
598
+ if (bTargetChanged)
599
+ {
600
+ RestoreTargets.Add(MCPRuntimeVisibilityRestoreActor(Target));
601
+ if (!bDryRun) Target.Actor->SetActorHiddenInGame(bHidden);
602
+ }
603
+ }
604
+ }
605
+
606
+ auto Result = MCPSuccess();
607
+ Result->SetBoolField(TEXT("hidden"), bHidden);
608
+ Result->SetBoolField(TEXT("dryRun"), bDryRun);
609
+ Result->SetBoolField(TEXT("mutationPerformed"), !bDryRun && Changed > 0);
610
+ Result->SetNumberField(TEXT("matchedActors"), SelectedActors.Num());
611
+ Result->SetNumberField(TEXT("targetCount"), Targets.Num());
612
+ Result->SetNumberField(TEXT("changed"), Changed);
613
+ Result->SetNumberField(TEXT("alreadyDesired"), Targets.Num() - Changed);
614
+ Result->SetStringField(TEXT("worldPath"), World->GetPathName());
615
+ Result->SetNumberField(TEXT("pieInstance"), WorldContext->PIEInstance);
616
+ Result->SetStringField(TEXT("netMode"), DescribePIENetMode(World));
617
+ if (bAffectActor)
618
+ {
619
+ Result->SetStringField(
620
+ TEXT("actorVisibilityNote"),
621
+ TEXT("Actor hidden state may be authority-owned or replicated; select the intended PIE instance."));
622
+ }
623
+ Result->SetArrayField(TEXT("targets"), Affected);
624
+
625
+ if (!bDryRun && RestoreTargets.Num() > 0)
626
+ {
627
+ TSharedPtr<FJsonObject> RestorePayload = MakeShared<FJsonObject>();
628
+ RestorePayload->SetStringField(TEXT("world"), TEXT("pie"));
629
+ RestorePayload->SetNumberField(TEXT("pieInstance"), WorldContext->PIEInstance);
630
+ RestorePayload->SetStringField(
631
+ TEXT("rollbackToken"),
632
+ MCPRuntimeVisibilityStoreRollback(World, WorldContext->PIEInstance, RestoreTargets));
633
+ MCPSetRollback(Result, TEXT("restore_runtime_visibility"), RestorePayload);
634
+ }
635
+ return MCPResult(Result);
636
+ }
637
+
638
+ TSharedPtr<FJsonValue> FEditorHandlers::RestoreRuntimeVisibility(const TSharedPtr<FJsonObject>& Params)
639
+ {
640
+ MCP_CHECK_GAME_THREAD();
641
+ if (!Params.IsValid()) return MCPError(TEXT("Missing params"));
642
+
643
+ FString RollbackToken;
644
+ if (!Params->TryGetStringField(TEXT("rollbackToken"), RollbackToken) ||
645
+ RollbackToken.TrimStartAndEnd().IsEmpty())
646
+ {
647
+ return MCPError(TEXT("Restore requires non-empty 'rollbackToken' from the set response"));
648
+ }
649
+ RollbackToken = RollbackToken.TrimStartAndEnd();
650
+ MCPRuntimeVisibilityRemoveExpiredRollbacks();
651
+ FMCPRuntimeVisibilityRollbackSnapshot* Snapshot =
652
+ GMCPRuntimeVisibilityRollbacks.Find(RollbackToken);
653
+ if (!Snapshot)
654
+ {
655
+ return MCPError(TEXT("Rollback token was not found or has expired"));
656
+ }
657
+
658
+ UWorld* World = Snapshot->World.Get();
659
+ const FWorldContext* WorldContext = nullptr;
660
+ if (GEngine && World)
661
+ {
662
+ for (const FWorldContext& Context : GEngine->GetWorldContexts())
663
+ {
664
+ if (Context.WorldType == EWorldType::PIE && Context.World() == World &&
665
+ Context.PIEInstance == Snapshot->PIEInstance)
666
+ {
667
+ WorldContext = &Context;
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ if (!World || !WorldContext)
673
+ {
674
+ GMCPRuntimeVisibilityRollbacks.Remove(RollbackToken);
675
+ return MCPError(TEXT("Rollback token belongs to an expired PIE session"));
676
+ }
677
+
678
+ if (Params->HasField(TEXT("world")))
679
+ {
680
+ FString WorldScope;
681
+ if (!Params->TryGetStringField(TEXT("world"), WorldScope) ||
682
+ !WorldScope.Equals(TEXT("pie"), ESearchCase::IgnoreCase))
683
+ {
684
+ return MCPError(TEXT("Runtime visibility restore is PIE-only; 'world' must be 'pie'"));
685
+ }
686
+ }
687
+ if (Params->HasField(TEXT("pieInstance")))
688
+ {
689
+ double RawPIEInstance = 0.0;
690
+ if (!Params->TryGetNumberField(TEXT("pieInstance"), RawPIEInstance) ||
691
+ !FMath::IsNearlyEqual(RawPIEInstance, FMath::RoundToInt(RawPIEInstance)))
692
+ {
693
+ return MCPError(TEXT("'pieInstance' must be an integer"));
694
+ }
695
+ if (FMath::RoundToInt(RawPIEInstance) != Snapshot->PIEInstance)
696
+ {
697
+ return MCPError(TEXT("'pieInstance' does not match the rollback token's PIE session"));
698
+ }
699
+ }
700
+
701
+ for (const FMCPRuntimeVisibilityRestoreTarget& Target : Snapshot->Targets)
702
+ {
703
+ if (Target.Component.IsValid())
704
+ {
705
+ if (Target.Component->GetWorld() != World)
706
+ {
707
+ GMCPRuntimeVisibilityRollbacks.Remove(RollbackToken);
708
+ return MCPError(TEXT("Rollback component no longer belongs to the original PIE session"));
709
+ }
710
+ }
711
+ else if (Target.Actor.IsValid())
712
+ {
713
+ if (Target.Actor->GetWorld() != World)
714
+ {
715
+ GMCPRuntimeVisibilityRollbacks.Remove(RollbackToken);
716
+ return MCPError(TEXT("Rollback actor no longer belongs to the original PIE session"));
717
+ }
718
+ }
719
+ else
720
+ {
721
+ GMCPRuntimeVisibilityRollbacks.Remove(RollbackToken);
722
+ return MCPError(TEXT("Rollback target was destroyed; no state was restored"));
723
+ }
724
+ }
725
+
726
+ int32 Changed = 0;
727
+ for (const FMCPRuntimeVisibilityRestoreTarget& Target : Snapshot->Targets)
728
+ {
729
+ if (USceneComponent* Component = Target.Component.Get())
730
+ {
731
+ if (Component->GetVisibleFlag() != Target.bVisible ||
732
+ (Component->bHiddenInGame != 0) != Target.bComponentHidden)
733
+ {
734
+ ++Changed;
735
+ Component->SetVisibility(Target.bVisible, false);
736
+ Component->SetHiddenInGame(Target.bComponentHidden, false);
737
+ }
738
+ }
739
+ else if (AActor* Actor = Target.Actor.Get(); Actor && Actor->IsHidden() != Target.bActorHidden)
740
+ {
741
+ ++Changed;
742
+ Actor->SetActorHiddenInGame(Target.bActorHidden);
743
+ }
744
+ }
745
+
746
+ auto Result = MCPSuccess();
747
+ Result->SetBoolField(TEXT("restored"), true);
748
+ Result->SetStringField(TEXT("rollbackToken"), RollbackToken);
749
+ Result->SetNumberField(TEXT("targetCount"), Snapshot->Targets.Num());
750
+ Result->SetNumberField(TEXT("changed"), Changed);
751
+ Result->SetNumberField(TEXT("alreadyRestored"), Snapshot->Targets.Num() - Changed);
752
+ Result->SetStringField(TEXT("worldPath"), World->GetPathName());
753
+ Result->SetNumberField(TEXT("pieInstance"), WorldContext->PIEInstance);
754
+ Result->SetStringField(TEXT("netMode"), DescribePIENetMode(World));
755
+ Result->SetBoolField(TEXT("persisted"), false);
756
+ return MCPResult(Result);
757
+ }