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.
Files changed (255) 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/index.js +41 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/lean-context.js +8 -3
  36. package/dist/lean-context.js.map +1 -1
  37. package/dist/locking.js +11 -2
  38. package/dist/locking.js.map +1 -1
  39. package/dist/plugin/loader.d.ts +4 -0
  40. package/dist/plugin/loader.js +22 -8
  41. package/dist/plugin/loader.js.map +1 -1
  42. package/dist/plugin/manifest.d.ts +72 -52
  43. package/dist/plugin/manifest.js +118 -21
  44. package/dist/plugin/manifest.js.map +1 -1
  45. package/dist/plugin-cli.js +16 -3
  46. package/dist/plugin-cli.js.map +1 -1
  47. package/dist/project.d.ts +11 -0
  48. package/dist/project.js +23 -2
  49. package/dist/project.js.map +1 -1
  50. package/dist/python-gate.d.ts +95 -0
  51. package/dist/python-gate.js +175 -0
  52. package/dist/python-gate.js.map +1 -0
  53. package/dist/tool-counts.json +16 -16
  54. package/dist/tools/animation.js +288 -24
  55. package/dist/tools/animation.js.map +1 -1
  56. package/dist/tools/asset.js +79 -14
  57. package/dist/tools/asset.js.map +1 -1
  58. package/dist/tools/audio.js +59 -5
  59. package/dist/tools/audio.js.map +1 -1
  60. package/dist/tools/blueprint.js +29 -12
  61. package/dist/tools/blueprint.js.map +1 -1
  62. package/dist/tools/chooser.js +4 -1
  63. package/dist/tools/chooser.js.map +1 -1
  64. package/dist/tools/editor.js +81 -43
  65. package/dist/tools/editor.js.map +1 -1
  66. package/dist/tools/feedback.js +214 -48
  67. package/dist/tools/feedback.js.map +1 -1
  68. package/dist/tools/foliage.js +27 -1
  69. package/dist/tools/foliage.js.map +1 -1
  70. package/dist/tools/gameplay.js +38 -11
  71. package/dist/tools/gameplay.js.map +1 -1
  72. package/dist/tools/gas.js +17 -5
  73. package/dist/tools/gas.js.map +1 -1
  74. package/dist/tools/landscape.js +22 -7
  75. package/dist/tools/landscape.js.map +1 -1
  76. package/dist/tools/level.js +322 -51
  77. package/dist/tools/level.js.map +1 -1
  78. package/dist/tools/material.js +10 -4
  79. package/dist/tools/material.js.map +1 -1
  80. package/dist/tools/niagara.js +3 -2
  81. package/dist/tools/niagara.js.map +1 -1
  82. package/dist/tools/pcg.js +6 -5
  83. package/dist/tools/pcg.js.map +1 -1
  84. package/dist/tools/plugins.js +2 -0
  85. package/dist/tools/plugins.js.map +1 -1
  86. package/dist/tools/project.js +47 -20
  87. package/dist/tools/project.js.map +1 -1
  88. package/dist/types.d.ts +24 -0
  89. package/dist/types.js +46 -3
  90. package/dist/types.js.map +1 -1
  91. package/dist/ue-mcp.default.yml +493 -125
  92. package/package.json +2 -2
  93. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  94. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  95. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  242. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  243. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  244. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  245. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  246. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  247. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  248. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  249. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  250. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  251. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  252. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  253. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  254. package/skills/ue-mcp-animation/SKILL.md +154 -0
  255. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -3,10 +3,16 @@
3
3
  #include "HandlerRegistry.h"
4
4
  #include "HandlerUtils.h"
5
5
  #include "HandlerJsonProperty.h"
6
+ #include "JsonSerializer.h"
6
7
  #include "Kismet2/BlueprintEditorUtils.h"
7
8
  #include "Kismet2/KismetEditorUtilities.h"
8
9
  #include "BlueprintEditorLibrary.h"
9
10
  #include "Engine/Blueprint.h"
11
+ #include "Engine/BlueprintGeneratedClass.h"
12
+ // #942: World -> level script Blueprint resolution.
13
+ #include "Engine/World.h"
14
+ #include "Engine/Level.h"
15
+ #include "Engine/LevelScriptBlueprint.h"
10
16
  #include "EdGraph/EdGraphPin.h"
11
17
  #include "EdGraphSchema_K2.h"
12
18
  #include "K2Node.h"
@@ -17,6 +23,7 @@
17
23
  #include "Editor.h"
18
24
  #include "Editor/EditorEngine.h"
19
25
  #include "UObject/UObjectGlobals.h"
26
+ #include "UObject/UObjectHash.h"
20
27
  #include "UObject/UnrealType.h"
21
28
  #include "UObject/Package.h"
22
29
  #include "Misc/PackageName.h"
@@ -25,6 +32,7 @@
25
32
  #include "UObject/TopLevelAssetPath.h"
26
33
  #include "AssetToolsModule.h"
27
34
  #include "IAssetTools.h"
35
+ #include "PackageTools.h"
28
36
  #include "Factories/BlueprintFactory.h"
29
37
  #include "EdGraph/EdGraph.h"
30
38
  #include "K2Node_CallFunction.h"
@@ -35,6 +43,7 @@
35
43
  #include "K2Node_EditablePinBase.h"
36
44
  #include "K2Node_IfThenElse.h"
37
45
  #include "K2Node_MacroInstance.h"
46
+ #include "K2Node_AddComponent.h"
38
47
  #include "K2Node_VariableGet.h"
39
48
  #include "K2Node_DynamicCast.h"
40
49
  #include "K2Node_VariableSet.h"
@@ -69,6 +78,10 @@
69
78
  void FBlueprintHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
70
79
  {
71
80
  constexpr float ReadBlueprintGraphTimeoutSeconds = 180.0f;
81
+ // #945: a first sweep on a cold project pays for every package load the
82
+ // registry could not rule out, which the default request timeout does not
83
+ // come close to covering.
84
+ constexpr float SearchCallSitesTimeoutSeconds = 600.0f;
72
85
 
73
86
  Registry.RegisterHandler(TEXT("create_blueprint"), &CreateBlueprint);
74
87
  Registry.RegisterHandler(TEXT("read_blueprint"), &ReadBlueprint);
@@ -107,6 +120,7 @@ void FBlueprintHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
107
120
  Registry.RegisterHandler(TEXT("delete_variable"), &DeleteVariable);
108
121
  Registry.RegisterHandler(TEXT("add_function_parameter"), &AddFunctionParameter);
109
122
  Registry.RegisterHandler(TEXT("set_variable_default"), &SetVariableDefault);
123
+ Registry.RegisterHandler(TEXT("get_blueprint_variable_default"), &GetVariableDefault);
110
124
 
111
125
  // v0.7.8 stubs
112
126
  Registry.RegisterHandler(TEXT("read_blueprint_graph_summary"), &ReadBlueprintGraphSummary);
@@ -125,6 +139,7 @@ void FBlueprintHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
125
139
  Registry.RegisterHandler(TEXT("reparent_component"), &ReparentComponent);
126
140
  Registry.RegisterHandler(TEXT("reparent_blueprint"), &ReparentBlueprint);
127
141
  Registry.RegisterHandler(TEXT("flush_inheritable_component_handler"), &FlushInheritableComponentHandler);
142
+ Registry.RegisterHandler(TEXT("flush_blueprint_component_templates"), &FlushComponentTemplates);
128
143
  Registry.RegisterHandler(TEXT("set_actor_tick_settings"), &SetActorTickSettings);
129
144
 
130
145
  // v0.7.12 - issue #128 - single-property read (inherited-aware)
@@ -147,6 +162,9 @@ void FBlueprintHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
147
162
  Registry.RegisterHandler(TEXT("connect_pins_batch"), &ConnectPinsBatch);
148
163
  Registry.RegisterHandler(TEXT("set_node_position"), &SetNodePosition);
149
164
  Registry.RegisterHandler(TEXT("auto_layout_graph"), &AutoLayoutGraph);
165
+
166
+ // #945: project-wide call-site audit (BlueprintHandlers_Search.cpp).
167
+ Registry.RegisterHandlerWithTimeout(TEXT("search_blueprint_call_sites"), &SearchCallSites, SearchCallSitesTimeoutSeconds);
150
168
  }
151
169
 
152
170
  // ---------------------------------------------------------------------------
@@ -166,7 +184,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprintGraphSummary(const TShar
166
184
  const bool bFiltering = !TitleFilter.IsEmpty() || !ClassFilter.IsEmpty();
167
185
 
168
186
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
169
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
187
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
170
188
 
171
189
  UEdGraph* Graph = FindGraph(Blueprint, GraphName);
172
190
  if (!Graph) return MCPError(FString::Printf(TEXT("Graph not found: %s"), *GraphName));
@@ -214,6 +232,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprintGraphSummary(const TShar
214
232
 
215
233
  TSharedPtr<FJsonObject> Result = MCPSuccess();
216
234
  Result->SetStringField(TEXT("path"), AssetPath);
235
+ AnnotateResolvedBlueprint(Result, Blueprint);
217
236
  Result->SetStringField(TEXT("graphName"), GraphName);
218
237
  // #298: identify graph type so callers can tell ubergraph / construction
219
238
  // script / function / macro apart without having to grep node titles.
@@ -254,7 +273,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::GetBlueprintExecutionFlow(const TShar
254
273
  FString EntryPoint = OptionalString(Params, TEXT("entryPoint"), TEXT(""));
255
274
 
256
275
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
257
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
276
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
258
277
 
259
278
  UEdGraph* Graph = FindGraph(Blueprint, GraphName);
260
279
  if (!Graph) return MCPError(FString::Printf(TEXT("Graph not found: %s"), *GraphName));
@@ -336,6 +355,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::GetBlueprintExecutionFlow(const TShar
336
355
 
337
356
  TSharedPtr<FJsonObject> Result = MCPSuccess();
338
357
  Result->SetStringField(TEXT("path"), AssetPath);
358
+ AnnotateResolvedBlueprint(Result, Blueprint);
339
359
  Result->SetStringField(TEXT("graphName"), GraphName);
340
360
  Result->SetStringField(TEXT("entryPoint"), Entry->GetNodeTitle(ENodeTitleType::ListView).ToString());
341
361
  Result->SetStringField(TEXT("entryId"), Entry->NodeGuid.ToString(EGuidFormats::Short));
@@ -351,7 +371,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::GetBlueprintDependencies(const TShare
351
371
  const bool bReverse = OptionalBool(Params, TEXT("reverse"), false);
352
372
 
353
373
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
354
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
374
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
355
375
 
356
376
  FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
357
377
  IAssetRegistry& Registry = AssetRegistryModule.Get();
@@ -433,9 +453,52 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::GetBlueprintDependencies(const TShare
433
453
  return MCPResult(Result);
434
454
  }
435
455
 
456
+ namespace
457
+ {
458
+ // #942: resolve a World/umap path to the level script Blueprint that lives
459
+ // inside it. The level script is a subobject of the persistent level, never
460
+ // an asset of its own, so loading a UBlueprint from "/Game/Maps/SomeLevel"
461
+ // can never find it however the path is spelled.
462
+ //
463
+ // bDontCreate is deliberate. A map that has never had a Level Blueprint
464
+ // opened has no level script object, and a READ must not author one as a
465
+ // side effect: it would dirty the map package and write a new subobject
466
+ // into somebody's level for asking a question about it.
467
+ ULevelScriptBlueprint* ResolveLevelScriptBlueprint(const FString& AssetPath)
468
+ {
469
+ UWorld* World = LoadAssetByPath<UWorld>(AssetPath);
470
+ if (!World || !World->PersistentLevel) return nullptr;
471
+ return World->PersistentLevel->GetLevelScriptBlueprint(/*bDontCreate=*/true);
472
+ }
473
+ }
474
+
436
475
  UBlueprint* FBlueprintHandlers::LoadBlueprint(const FString& AssetPath)
437
476
  {
438
- return LoadAssetByPath<UBlueprint>(AssetPath);
477
+ if (UBlueprint* Direct = LoadAssetByPath<UBlueprint>(AssetPath))
478
+ {
479
+ return Direct;
480
+ }
481
+ // #942: one resolution point, so every action that reaches a Blueprint
482
+ // through this function accepts a umap path on exactly the same terms.
483
+ return ResolveLevelScriptBlueprint(AssetPath);
484
+ }
485
+
486
+ TSharedPtr<FJsonValue> BlueprintNotFoundError(const FString& AssetPath)
487
+ {
488
+ if (UWorld* World = LoadAssetByPath<UWorld>(AssetPath))
489
+ {
490
+ return MCPError(FString::Printf(
491
+ TEXT("'%s' is a World, and its level script Blueprint does not exist yet, so there is nothing to read. Open the map's Level Blueprint in the editor once (that creates it), then retry this call with the same path. When it exists it resolves to %s:PersistentLevel.%s"),
492
+ *AssetPath, *World->GetPathName(), *World->GetName()));
493
+ }
494
+ return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
495
+ }
496
+
497
+ void AnnotateResolvedBlueprint(const TSharedPtr<FJsonObject>& Result, UBlueprint* Blueprint)
498
+ {
499
+ if (!Result.IsValid() || !Blueprint) return;
500
+ Result->SetStringField(TEXT("blueprintPath"), Blueprint->GetPathName());
501
+ Result->SetBoolField(TEXT("isLevelScript"), Blueprint->IsA<ULevelScriptBlueprint>());
439
502
  }
440
503
 
441
504
  // ---------------------------------------------------------------------------
@@ -451,9 +514,9 @@ namespace
451
514
  const FString Name = Graph->GetName();
452
515
  const int32 DuplicateIndex = SeenCounts.FindOrAdd(Name)++;
453
516
  const int32 DuplicateCount = NameCounts.FindRef(Name);
454
- const FString Selector = DuplicateCount > 1
455
- ? FString::Printf(TEXT("%s[%d]"), *Name, DuplicateIndex)
456
- : Name;
517
+ // #945: one selector rule, shared with search_call_sites so the two
518
+ // cannot disagree about how to address the same graph.
519
+ const FString Selector = MakeGraphSelector(Name, DuplicateIndex, DuplicateCount);
457
520
 
458
521
  TSharedPtr<FJsonObject> GraphObj = MakeShared<FJsonObject>();
459
522
  GraphObj->SetStringField(TEXT("name"), Name);
@@ -465,17 +528,6 @@ namespace
465
528
  GraphObj->SetNumberField(TEXT("duplicateCount"), DuplicateCount);
466
529
  return GraphObj;
467
530
  }
468
-
469
- void CountGraphNames(const TArray<UEdGraph*>& Graphs, TMap<FString, int32>& OutNameCounts)
470
- {
471
- for (UEdGraph* Graph : Graphs)
472
- {
473
- if (Graph)
474
- {
475
- ++OutNameCounts.FindOrAdd(Graph->GetName());
476
- }
477
- }
478
- }
479
531
  }
480
532
 
481
533
  TSharedPtr<FJsonValue> FBlueprintHandlers::ListGraphs(const TSharedPtr<FJsonObject>& Params)
@@ -486,7 +538,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ListGraphs(const TSharedPtr<FJsonObje
486
538
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
487
539
  if (!Blueprint)
488
540
  {
489
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
541
+ return BlueprintNotFoundError(AssetPath);
490
542
  }
491
543
 
492
544
  TArray<UEdGraph*> AllGraphs;
@@ -505,6 +557,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ListGraphs(const TSharedPtr<FJsonObje
505
557
 
506
558
  auto Result = MCPSuccess();
507
559
  Result->SetStringField(TEXT("path"), AssetPath);
560
+ AnnotateResolvedBlueprint(Result, Blueprint);
508
561
  Result->SetArrayField(TEXT("graphs"), GraphsArray);
509
562
 
510
563
  return MCPResult(Result);
@@ -527,7 +580,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ResolveGraph(const TSharedPtr<FJsonOb
527
580
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
528
581
  if (!Blueprint)
529
582
  {
530
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
583
+ return BlueprintNotFoundError(AssetPath);
531
584
  }
532
585
 
533
586
  TArray<UEdGraph*> AllGraphs;
@@ -588,6 +641,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ResolveGraph(const TSharedPtr<FJsonOb
588
641
 
589
642
  TSharedPtr<FJsonObject> Result = MCPSuccess();
590
643
  Result->SetStringField(TEXT("path"), AssetPath);
644
+ AnnotateResolvedBlueprint(Result, Blueprint);
591
645
  Result->SetStringField(TEXT("requestedGraphName"), RequestedName);
592
646
  Result->SetNumberField(TEXT("matchCount"), MatchArray.Num());
593
647
  Result->SetBoolField(TEXT("ambiguous"), MatchArray.Num() > 1);
@@ -1122,7 +1176,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprint(const TSharedPtr<FJsonO
1122
1176
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1123
1177
  if (!Blueprint)
1124
1178
  {
1125
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1179
+ return BlueprintNotFoundError(AssetPath);
1126
1180
  }
1127
1181
 
1128
1182
  // #353/#370: per-component property dump on demand. Off by default so the
@@ -1153,6 +1207,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprint(const TSharedPtr<FJsonO
1153
1207
 
1154
1208
  auto Result = MCPSuccess();
1155
1209
  Result->SetStringField(TEXT("path"), AssetPath);
1210
+ AnnotateResolvedBlueprint(Result, Blueprint);
1156
1211
  Result->SetStringField(TEXT("className"), Blueprint->GetName());
1157
1212
  if (Blueprint->ParentClass)
1158
1213
  {
@@ -1331,7 +1386,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::AddVariable(const TSharedPtr<FJsonObj
1331
1386
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1332
1387
  if (!Blueprint)
1333
1388
  {
1334
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1389
+ return BlueprintNotFoundError(AssetPath);
1335
1390
  }
1336
1391
 
1337
1392
  // Idempotency: if the variable already exists on the blueprint, short-circuit.
@@ -1399,7 +1454,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::AddComponent(const TSharedPtr<FJsonOb
1399
1454
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1400
1455
  if (!Blueprint)
1401
1456
  {
1402
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1457
+ return BlueprintNotFoundError(AssetPath);
1403
1458
  }
1404
1459
 
1405
1460
  // Idempotency: existing SCS component with same name short-circuits.
@@ -1562,7 +1617,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::CompileBlueprint(const TSharedPtr<FJs
1562
1617
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1563
1618
  if (!Blueprint)
1564
1619
  {
1565
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1620
+ return BlueprintNotFoundError(AssetPath);
1566
1621
  }
1567
1622
 
1568
1623
  // #703: capture the compiler log and report real status instead of always
@@ -1943,15 +1998,217 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ListNodeTypes(const TSharedPtr<FJsonO
1943
1998
  return MCPResult(Result);
1944
1999
  }
1945
2000
 
2001
+ // ---------------------------------------------------------------------------
2002
+ // #902 / #931: resolved variable defaults, and whether they are on disk.
2003
+ //
2004
+ // list_variables could confirm a variable existed but nothing returned the
2005
+ // value the generated class actually resolved to, so a write-compile-readback
2006
+ // loop was impossible natively and callers dropped to Python for
2007
+ // get_default_object(bp.generated_class()).get_editor_property(name).
2008
+ //
2009
+ // The value lives on the CDO, not on FBPVariableDescription::DefaultValue. The
2010
+ // engine documents that string as an "optional new default value", and it is
2011
+ // empty for most variables, so reading it answers a different question than
2012
+ // the one being asked. Anything that reads it instead reports an empty default
2013
+ // for a variable that plainly has one.
2014
+ //
2015
+ // Persistence is reported separately, and that separation is the point (#931).
2016
+ // A write that sets a CDO property and marks the package dirty without saving
2017
+ // it reads back correctly for the rest of the session and is gone on the next
2018
+ // editor start. UPackage::IsDirty is the engine's own record of exactly that
2019
+ // state, so `persisted` is false while the package holds unsaved changes: the
2020
+ // readback distinguishes "this value is on disk" from "this value is in this
2021
+ // process", instead of echoing the write back at the caller either way.
2022
+ // ---------------------------------------------------------------------------
2023
+ namespace
2024
+ {
2025
+ struct FResolvedVariableDefault
2026
+ {
2027
+ FProperty* Property = nullptr;
2028
+ const void* ValueAddress = nullptr;
2029
+ FString ValueText;
2030
+ TSharedPtr<FJsonValue> Value;
2031
+ FString DeclaringClass;
2032
+ FString DeclaringClassPath;
2033
+ bool bInherited = false;
2034
+ };
2035
+
2036
+ // Resolve one variable's compiled default off the Blueprint's generated
2037
+ // class CDO. Returns false with a caller-facing reason on any miss.
2038
+ bool ResolveVariableDefault(
2039
+ UBlueprint* Blueprint,
2040
+ const FString& VarName,
2041
+ FResolvedVariableDefault& Out,
2042
+ FString& OutError)
2043
+ {
2044
+ if (!Blueprint)
2045
+ {
2046
+ OutError = TEXT("No Blueprint to resolve a variable default from");
2047
+ return false;
2048
+ }
2049
+
2050
+ UClass* GeneratedClass = Blueprint->GeneratedClass.Get();
2051
+ if (!GeneratedClass)
2052
+ {
2053
+ OutError = FString::Printf(
2054
+ TEXT("Blueprint '%s' has no generated class, so it has no resolved defaults yet. Compile it first (blueprint compile)."),
2055
+ *Blueprint->GetName());
2056
+ return false;
2057
+ }
2058
+
2059
+ UObject* CDO = GeneratedClass->GetDefaultObject();
2060
+ if (!CDO)
2061
+ {
2062
+ OutError = FString::Printf(
2063
+ TEXT("Generated class '%s' has no class default object"), *GeneratedClass->GetName());
2064
+ return false;
2065
+ }
2066
+
2067
+ FProperty* Prop = GeneratedClass->FindPropertyByName(FName(*VarName));
2068
+ if (!Prop)
2069
+ {
2070
+ // Name the variables that DO resolve, so a caller that has just
2071
+ // added one can see whether the compile carried it through.
2072
+ TArray<FString> Available;
2073
+ for (TFieldIterator<FProperty> It(GeneratedClass); It && Available.Num() < 60; ++It)
2074
+ {
2075
+ if (*It) Available.Add((*It)->GetName());
2076
+ }
2077
+ OutError = FString::Printf(
2078
+ TEXT("Variable '%s' has no property on generated class '%s'. If it was just added, compile the Blueprint. Resolved properties: [%s]"),
2079
+ *VarName, *GeneratedClass->GetName(), *FString::Join(Available, TEXT(", ")));
2080
+ return false;
2081
+ }
2082
+
2083
+ Out.Property = Prop;
2084
+ Out.ValueAddress = Prop->ContainerPtrToValuePtr<void>(CDO);
2085
+ Prop->ExportText_Direct(Out.ValueText, Out.ValueAddress, Out.ValueAddress, CDO, PPF_None);
2086
+ Out.Value = FMCPJsonSerializer::SerializeValue(Out.ValueAddress, Prop);
2087
+
2088
+ if (UClass* Owner = Prop->GetOwnerClass())
2089
+ {
2090
+ Out.DeclaringClass = Owner->GetName();
2091
+ Out.DeclaringClassPath = Owner->GetPathName();
2092
+ Out.bInherited = Owner != GeneratedClass;
2093
+ }
2094
+ return true;
2095
+ }
2096
+
2097
+ // Write the resolved value onto a JSON object. Shared so list_variables and
2098
+ // get_variable_default cannot report the same value under different names.
2099
+ void WriteResolvedVariableDefault(const TSharedPtr<FJsonObject>& Obj, const FResolvedVariableDefault& Resolved)
2100
+ {
2101
+ if (!Obj.IsValid() || !Resolved.Property) return;
2102
+ Obj->SetField(TEXT("value"), Resolved.Value.IsValid() ? Resolved.Value : MakeShared<FJsonValueNull>());
2103
+ Obj->SetStringField(TEXT("valueText"), Resolved.ValueText);
2104
+ Obj->SetStringField(TEXT("cppType"), Resolved.Property->GetCPPType());
2105
+ if (!Resolved.DeclaringClass.IsEmpty())
2106
+ {
2107
+ Obj->SetStringField(TEXT("declaringClass"), Resolved.DeclaringClass);
2108
+ Obj->SetStringField(TEXT("declaringClassPath"), Resolved.DeclaringClassPath);
2109
+ }
2110
+ Obj->SetBoolField(TEXT("inherited"), Resolved.bInherited);
2111
+ }
2112
+
2113
+ // #931: state whether what was just read is on disk. The package's own
2114
+ // dirty flag is the answer: it is set by every write that reaches the
2115
+ // object and cleared by a successful save, so a value that reads back
2116
+ // correctly out of a dirty package has not been persisted and will be gone
2117
+ // after a restart.
2118
+ void WriteDefaultPersistence(const TSharedPtr<FJsonObject>& Obj, UBlueprint* Blueprint)
2119
+ {
2120
+ if (!Obj.IsValid() || !Blueprint) return;
2121
+ UPackage* Package = Blueprint->GetOutermost();
2122
+ const bool bDirty = Package && Package->IsDirty();
2123
+ if (Package)
2124
+ {
2125
+ Obj->SetStringField(TEXT("packageName"), Package->GetName());
2126
+ }
2127
+ Obj->SetBoolField(TEXT("packageDirty"), bDirty);
2128
+ Obj->SetBoolField(TEXT("persisted"), !bDirty);
2129
+ if (bDirty)
2130
+ {
2131
+ Obj->SetStringField(TEXT("persistenceNote"),
2132
+ TEXT("This value is live in the editor but its package has unsaved changes, so it is not on disk and will revert on the next editor start. Save the Blueprint (blueprint compile_all with save, or asset save) and read again to confirm it persisted."));
2133
+ }
2134
+ }
2135
+ }
2136
+
2137
+ TSharedPtr<FJsonValue> FBlueprintHandlers::GetVariableDefault(const TSharedPtr<FJsonObject>& Params)
2138
+ {
2139
+ FString AssetPath;
2140
+ if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
2141
+
2142
+ FString VarName;
2143
+ if (auto Err = RequireString(Params, TEXT("name"), VarName)) return Err;
2144
+
2145
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2146
+ if (!Blueprint)
2147
+ {
2148
+ return BlueprintNotFoundError(AssetPath);
2149
+ }
2150
+
2151
+ FResolvedVariableDefault Resolved;
2152
+ FString ResolveError;
2153
+ if (!ResolveVariableDefault(Blueprint, VarName, Resolved, ResolveError))
2154
+ {
2155
+ return MCPError(ResolveError);
2156
+ }
2157
+
2158
+ auto Result = MCPSuccess();
2159
+ Result->SetStringField(TEXT("path"), AssetPath);
2160
+ AnnotateResolvedBlueprint(Result, Blueprint);
2161
+ Result->SetStringField(TEXT("name"), VarName);
2162
+ WriteResolvedVariableDefault(Result, Resolved);
2163
+ WriteDefaultPersistence(Result, Blueprint);
2164
+
2165
+ // The authored string, when there is one, is reported alongside rather than
2166
+ // instead of the resolved value. It is advisory: the engine treats it as an
2167
+ // optional override, so an empty one is normal and says nothing. A non-empty
2168
+ // one that disagrees with the CDO means the next recompile can move the
2169
+ // value, which is worth seeing in a verification loop.
2170
+ for (const FBPVariableDescription& Var : Blueprint->NewVariables)
2171
+ {
2172
+ if (Var.VarName.ToString() != VarName) continue;
2173
+ Result->SetBoolField(TEXT("declaredOnThisBlueprint"), true);
2174
+ if (Var.DefaultValue.IsEmpty()) break;
2175
+
2176
+ Result->SetStringField(TEXT("authoredDefault"), Var.DefaultValue);
2177
+ FDefaultConstructedPropertyElement Authored(Resolved.Property);
2178
+ // No owning object on purpose: this is a read, and an owner is what
2179
+ // lets the importer construct instanced subobjects under the real
2180
+ // asset. A question about a value must not touch it.
2181
+ const bool bParsed = FBlueprintEditorUtils::PropertyValueFromString_Direct(
2182
+ Resolved.Property,
2183
+ Var.DefaultValue,
2184
+ static_cast<uint8*>(Authored.GetObjAddress()),
2185
+ /*OwningObject=*/nullptr);
2186
+ if (bParsed)
2187
+ {
2188
+ Result->SetBoolField(TEXT("matchesAuthoredDefault"),
2189
+ Resolved.Property->Identical(Resolved.ValueAddress, Authored.GetObjAddress(), PPF_None));
2190
+ }
2191
+ break;
2192
+ }
2193
+
2194
+ return MCPResult(Result);
2195
+ }
2196
+
1946
2197
  TSharedPtr<FJsonValue> FBlueprintHandlers::ListBlueprintVariables(const TSharedPtr<FJsonObject>& Params)
1947
2198
  {
1948
2199
  FString AssetPath;
1949
2200
  if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
1950
2201
 
2202
+ // #902: resolved values are opt-in. They cost a CDO property read and a
2203
+ // JSON serialization per variable, and every existing caller of this action
2204
+ // wants the declaration list, so the default payload is unchanged and a
2205
+ // verification loop asks for the values it needs.
2206
+ const bool bIncludeValues = OptionalBool(Params, TEXT("includeValues"), false);
2207
+
1951
2208
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1952
2209
  if (!Blueprint)
1953
2210
  {
1954
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2211
+ return BlueprintNotFoundError(AssetPath);
1955
2212
  }
1956
2213
 
1957
2214
  TArray<TSharedPtr<FJsonValue>> Variables;
@@ -2014,13 +2271,37 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ListBlueprintVariables(const TSharedP
2014
2271
  (Var.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn) &&
2015
2272
  Var.GetMetaData(FBlueprintMetadata::MD_ExposeOnSpawn).ToBool()));
2016
2273
 
2274
+ if (bIncludeValues)
2275
+ {
2276
+ FResolvedVariableDefault Resolved;
2277
+ FString ResolveError;
2278
+ if (ResolveVariableDefault(Blueprint, Var.VarName.ToString(), Resolved, ResolveError))
2279
+ {
2280
+ WriteResolvedVariableDefault(VarObj, Resolved);
2281
+ }
2282
+ else
2283
+ {
2284
+ // A variable that has no compiled property is a real state
2285
+ // (added but not compiled yet), so say so per variable rather
2286
+ // than failing the whole listing.
2287
+ VarObj->SetStringField(TEXT("valueError"), ResolveError);
2288
+ }
2289
+ }
2290
+
2017
2291
  Variables.Add(MakeShared<FJsonValueObject>(VarObj));
2018
2292
  }
2019
2293
 
2020
2294
  auto Result = MCPSuccess();
2021
2295
  Result->SetStringField(TEXT("path"), AssetPath);
2296
+ AnnotateResolvedBlueprint(Result, Blueprint);
2022
2297
  Result->SetArrayField(TEXT("variables"), Variables);
2023
2298
  Result->SetNumberField(TEXT("count"), Variables.Num());
2299
+ if (bIncludeValues)
2300
+ {
2301
+ // Persistence is a property of the package, not of any one variable, so
2302
+ // it is stated once for the whole listing.
2303
+ WriteDefaultPersistence(Result, Blueprint);
2304
+ }
2024
2305
  return MCPResult(Result);
2025
2306
  }
2026
2307
  TSharedPtr<FJsonValue> FBlueprintHandlers::RemoveComponent(const TSharedPtr<FJsonObject>& Params)
@@ -2034,7 +2315,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::RemoveComponent(const TSharedPtr<FJso
2034
2315
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2035
2316
  if (!Blueprint)
2036
2317
  {
2037
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2318
+ return BlueprintNotFoundError(AssetPath);
2038
2319
  }
2039
2320
 
2040
2321
  USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript;
@@ -2129,7 +2410,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::DeleteVariable(const TSharedPtr<FJson
2129
2410
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2130
2411
  if (!Blueprint)
2131
2412
  {
2132
- return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2413
+ return BlueprintNotFoundError(AssetPath);
2133
2414
  }
2134
2415
 
2135
2416
  bool bFound = false;
@@ -2210,7 +2491,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::AddLocalVariable(const TSharedPtr<FJs
2210
2491
  FString TypeStr = OptionalString(Params, TEXT("varType"), TEXT("bool"));
2211
2492
 
2212
2493
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2213
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2494
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
2214
2495
 
2215
2496
  // Find the function graph and its FunctionEntry node.
2216
2497
  UEdGraph* FuncGraph = nullptr;
@@ -2276,7 +2557,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ListLocalVariables(const TSharedPtr<F
2276
2557
  if (auto Err = RequireString(Params, TEXT("functionName"), FunctionName)) return Err;
2277
2558
 
2278
2559
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2279
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2560
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
2280
2561
 
2281
2562
  UEdGraph* FuncGraph = nullptr;
2282
2563
  for (UEdGraph* G : Blueprint->FunctionGraphs)
@@ -2317,7 +2598,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ValidateBlueprint(const TSharedPtr<FJ
2317
2598
  if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
2318
2599
 
2319
2600
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2320
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2601
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
2321
2602
 
2322
2603
  // Run compile without saving; collect diagnostics from the compiler result log.
2323
2604
  FCompilerResultsLog Log;
@@ -2353,6 +2634,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReparentComponent(const TSharedPtr<FJ
2353
2634
 
2354
2635
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2355
2636
  if (!Blueprint) return MCPError(TEXT("Blueprint not found"));
2637
+ if (auto Blocked = MCPAssetWriteBlockedError(Blueprint, AssetPath, TEXT("reparent this component"))) return Blocked;
2356
2638
  USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript;
2357
2639
  if (!SCS) return MCPError(TEXT("Blueprint has no SCS"));
2358
2640
 
@@ -2370,12 +2652,15 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReparentComponent(const TSharedPtr<FJ
2370
2652
  Parent->AddChildNode(Child);
2371
2653
 
2372
2654
  FKismetEditorUtilities::CompileBlueprint(Blueprint);
2373
- SaveAssetPackage(Blueprint);
2655
+
2656
+ FString SaveReason;
2657
+ const bool bSaved = SaveAssetPackageChecked(Blueprint, SaveReason);
2374
2658
 
2375
2659
  auto Result = MCPSuccess();
2376
2660
  MCPSetUpdated(Result);
2377
2661
  Result->SetStringField(TEXT("componentName"), ComponentName);
2378
2662
  Result->SetStringField(TEXT("newParent"), NewParent);
2663
+ MCPNoteSaveOutcome(Result, AssetPath, bSaved, SaveReason);
2379
2664
  return MCPResult(Result);
2380
2665
  }
2381
2666
 
@@ -2390,7 +2675,16 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReparentBlueprint(const TSharedPtr<FJ
2390
2675
  if (auto Err = RequireString(Params, TEXT("parentClass"), ParentClassName)) return Err;
2391
2676
 
2392
2677
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2393
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2678
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
2679
+
2680
+ // #932: reparenting reparents, recompiles AND saves, and the save is not
2681
+ // optional. A .uasset that was never checked out of source control is
2682
+ // read-only on disk, and asking the engine to write it turned the failed
2683
+ // save into a FATAL error that took the whole editor process down. The
2684
+ // asset itself was fine and the call replayed cleanly after a checkout, so
2685
+ // the only thing missing was this question, asked before the Blueprint is
2686
+ // touched rather than after it has already been reparented and recompiled.
2687
+ if (auto Blocked = MCPAssetWriteBlockedError(Blueprint, AssetPath, TEXT("reparent this Blueprint"))) return Blocked;
2394
2688
 
2395
2689
  // Resolve parent class: full path > short name > engine-module implicit.
2396
2690
  UClass* NewParent = nullptr;
@@ -2436,7 +2730,8 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReparentBlueprint(const TSharedPtr<FJ
2436
2730
  UBlueprintEditorLibrary::ReparentBlueprint(Blueprint, NewParent);
2437
2731
  FKismetEditorUtilities::CompileBlueprint(Blueprint);
2438
2732
 
2439
- SaveAssetPackage(Blueprint);
2733
+ FString SaveReason;
2734
+ const bool bSaved = SaveAssetPackageChecked(Blueprint, SaveReason);
2440
2735
 
2441
2736
  auto Result = MCPSuccess();
2442
2737
  MCPSetUpdated(Result);
@@ -2446,6 +2741,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::ReparentBlueprint(const TSharedPtr<FJ
2446
2741
  {
2447
2742
  Result->SetStringField(TEXT("previousParent"), OldParent->GetPathName());
2448
2743
  }
2744
+ MCPNoteSaveOutcome(Result, AssetPath, bSaved, SaveReason);
2449
2745
  return MCPResult(Result);
2450
2746
  }
2451
2747
 
@@ -2459,7 +2755,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::FlushInheritableComponentHandler(cons
2459
2755
  if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
2460
2756
 
2461
2757
  UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2462
- if (!Blueprint) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2758
+ if (!Blueprint) return BlueprintNotFoundError(AssetPath);
2463
2759
 
2464
2760
  UInheritableComponentHandler* ICH = Blueprint->GetInheritableComponentHandler(/*bCreateIfNecessary=*/false);
2465
2761
  if (!ICH)
@@ -2505,7 +2801,7 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::FlushInheritableComponentHandler(cons
2505
2801
  return MCPResult(Result);
2506
2802
  }
2507
2803
 
2508
- TSharedPtr<FJsonValue> FBlueprintHandlers::RunConstructionScript(const TSharedPtr<FJsonObject>& Params)
2804
+ TSharedPtr<FJsonValue> FBlueprintHandlers::FlushComponentTemplates(const TSharedPtr<FJsonObject>& Params)
2509
2805
  {
2510
2806
  FString AssetPath;
2511
2807
  if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
@@ -2516,6 +2812,157 @@ TSharedPtr<FJsonValue> FBlueprintHandlers::RunConstructionScript(const TSharedPt
2516
2812
  return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
2517
2813
  }
2518
2814
 
2815
+ TArray<UK2Node_AddComponent*> ComponentNodes;
2816
+ FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, ComponentNodes);
2817
+
2818
+ TArray<UActorComponent*> ReferencedTemplates;
2819
+ bool bNeedsUpdate = false;
2820
+ for (UK2Node_AddComponent* ComponentNode : ComponentNodes)
2821
+ {
2822
+ UActorComponent* Template = ComponentNode ? ComponentNode->GetTemplateFromNode() : nullptr;
2823
+ if (!Template)
2824
+ {
2825
+ continue;
2826
+ }
2827
+ if (ReferencedTemplates.Contains(Template))
2828
+ {
2829
+ bNeedsUpdate = true;
2830
+ continue;
2831
+ }
2832
+ ReferencedTemplates.Add(Template);
2833
+ bNeedsUpdate |= !Template->HasAllFlags(RF_ArchetypeObject | RF_Transactional);
2834
+ }
2835
+
2836
+ const int32 RecordsBefore = Blueprint->ComponentTemplates.Num();
2837
+ if (RecordsBefore != ReferencedTemplates.Num())
2838
+ {
2839
+ bNeedsUpdate = true;
2840
+ }
2841
+ else
2842
+ {
2843
+ for (int32 Index = 0; Index < RecordsBefore; ++Index)
2844
+ {
2845
+ if (Blueprint->ComponentTemplates[Index].Get() != ReferencedTemplates[Index])
2846
+ {
2847
+ bNeedsUpdate = true;
2848
+ break;
2849
+ }
2850
+ }
2851
+ }
2852
+
2853
+ TArray<TSharedPtr<FJsonValue>> RemovedTemplates;
2854
+ TArray<UActorComponent*> OrphanTemplates;
2855
+ for (UActorComponent* Template : Blueprint->ComponentTemplates)
2856
+ {
2857
+ if (Template && !ReferencedTemplates.Contains(Template))
2858
+ {
2859
+ OrphanTemplates.AddUnique(Template);
2860
+ }
2861
+ }
2862
+ if (Blueprint->GeneratedClass)
2863
+ {
2864
+ TArray<UObject*> OwnedObjects;
2865
+ GetObjectsWithOuter(Blueprint->GeneratedClass, OwnedObjects, EGetObjectsFlags::None);
2866
+ for (UObject* OwnedObject : OwnedObjects)
2867
+ {
2868
+ UActorComponent* Template = Cast<UActorComponent>(OwnedObject);
2869
+ if (Template
2870
+ && Template->GetName().StartsWith(UK2Node_AddComponent::ComponentTemplateNamePrefix)
2871
+ && !ReferencedTemplates.Contains(Template))
2872
+ {
2873
+ OrphanTemplates.AddUnique(Template);
2874
+ }
2875
+ }
2876
+ }
2877
+ bNeedsUpdate |= !OrphanTemplates.IsEmpty();
2878
+
2879
+ for (UActorComponent* Template : OrphanTemplates)
2880
+ {
2881
+ TSharedPtr<FJsonObject> Identity = MakeShared<FJsonObject>();
2882
+ Identity->SetStringField(TEXT("name"), Template->GetName());
2883
+ Identity->SetStringField(TEXT("objectPath"), Template->GetPathName());
2884
+ Identity->SetStringField(TEXT("classPath"), Template->GetClass()->GetPathName());
2885
+ RemovedTemplates.Add(MakeShared<FJsonValueObject>(Identity));
2886
+ }
2887
+
2888
+ if (bNeedsUpdate)
2889
+ {
2890
+ Blueprint->Modify();
2891
+ FBlueprintEditorUtils::UpdateComponentTemplates(Blueprint);
2892
+ if (UBlueprintGeneratedClass* GeneratedClass = Cast<UBlueprintGeneratedClass>(Blueprint->GeneratedClass))
2893
+ {
2894
+ for (UActorComponent* Template : OrphanTemplates)
2895
+ {
2896
+ GeneratedClass->ComponentTemplates.Remove(Template);
2897
+ }
2898
+ }
2899
+ for (UActorComponent* Template : OrphanTemplates)
2900
+ {
2901
+ Template->Modify();
2902
+ Template->ClearFlags(RF_Public | RF_Standalone);
2903
+ if (!Template->Rename(nullptr, GetTransientPackage(),
2904
+ REN_DoNotDirty | REN_DontCreateRedirectors | REN_AllowPackageLinkerMismatch | REN_NonTransactional))
2905
+ {
2906
+ return MCPError(FString::Printf(TEXT("Failed to retire orphan component template: %s"), *Template->GetPathName()));
2907
+ }
2908
+ }
2909
+ FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
2910
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
2911
+ const int32 RecordsAfter = Blueprint->ComponentTemplates.Num();
2912
+ if (!SaveAssetPackage(Blueprint))
2913
+ {
2914
+ return MCPError(FString::Printf(TEXT("Failed to save Blueprint after flushing component templates: %s"), *AssetPath));
2915
+ }
2916
+
2917
+ if (!OrphanTemplates.IsEmpty())
2918
+ {
2919
+ FText ReloadError;
2920
+ TArray<UPackage*> PackagesToReload{Blueprint->GetOutermost()};
2921
+ if (!UPackageTools::ReloadPackages(
2922
+ PackagesToReload, ReloadError, EReloadPackagesInteractionMode::AssumePositive))
2923
+ {
2924
+ return MCPError(FString::Printf(TEXT("Failed to reload Blueprint after retiring orphan templates: %s"), *ReloadError.ToString()));
2925
+ }
2926
+ Blueprint = LoadBlueprint(AssetPath);
2927
+ if (!Blueprint || !SaveAssetPackage(Blueprint))
2928
+ {
2929
+ return MCPError(FString::Printf(TEXT("Failed final Blueprint save after retiring orphan templates: %s"), *AssetPath));
2930
+ }
2931
+ }
2932
+
2933
+ auto Result = MCPSuccess();
2934
+ MCPSetUpdated(Result);
2935
+ Result->SetStringField(TEXT("path"), AssetPath);
2936
+ Result->SetNumberField(TEXT("recordsBefore"), RecordsBefore);
2937
+ Result->SetNumberField(TEXT("recordsAfter"), RecordsAfter);
2938
+ Result->SetNumberField(TEXT("recordsRemoved"), RemovedTemplates.Num());
2939
+ Result->SetArrayField(TEXT("removedTemplates"), RemovedTemplates);
2940
+ Result->SetBoolField(TEXT("reloadedAfterCleanup"), !OrphanTemplates.IsEmpty());
2941
+ return MCPResult(Result);
2942
+ }
2943
+
2944
+ auto Result = MCPSuccess();
2945
+ MCPSetExisted(Result);
2946
+ Result->SetStringField(TEXT("path"), AssetPath);
2947
+ Result->SetNumberField(TEXT("recordsBefore"), RecordsBefore);
2948
+ Result->SetNumberField(TEXT("recordsAfter"), Blueprint->ComponentTemplates.Num());
2949
+ Result->SetNumberField(TEXT("recordsRemoved"), RemovedTemplates.Num());
2950
+ Result->SetArrayField(TEXT("removedTemplates"), RemovedTemplates);
2951
+ Result->SetBoolField(TEXT("reloadedAfterCleanup"), false);
2952
+ return MCPResult(Result);
2953
+ }
2954
+
2955
+ TSharedPtr<FJsonValue> FBlueprintHandlers::RunConstructionScript(const TSharedPtr<FJsonObject>& Params)
2956
+ {
2957
+ FString AssetPath;
2958
+ if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
2959
+
2960
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
2961
+ if (!Blueprint)
2962
+ {
2963
+ return BlueprintNotFoundError(AssetPath);
2964
+ }
2965
+
2519
2966
  UClass* SpawnClass = Blueprint->GeneratedClass;
2520
2967
  if (!SpawnClass)
2521
2968
  {