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
@@ -0,0 +1,195 @@
1
+ // Coverage for level(query_components) that is safe to run anywhere.
2
+ //
3
+ // run_automation_tests dispatches every EditorContext/EngineFilter test in the
4
+ // process against whatever project the bridge is attached to, so nothing here
5
+ // may write. query_components never writes by design, which makes most of it
6
+ // testable directly: the assertions below run the real handler against the
7
+ // open editor world and check the contract (registration, argument rejection,
8
+ // bounded output, the read guarantee, clean enum names) rather than any
9
+ // particular level's contents.
10
+
11
+ #if WITH_DEV_AUTOMATION_TESTS
12
+
13
+ #include "HandlerRegistry.h"
14
+ #include "Handlers/LevelHandlers.h"
15
+ #include "Misc/AutomationTest.h"
16
+
17
+ namespace
18
+ {
19
+ TSharedPtr<FJsonObject> MCPQueryTestRun(FMCPHandlerRegistry& Registry, const TSharedPtr<FJsonObject>& Request)
20
+ {
21
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("query_components"), Request);
22
+ if (!Response.IsValid() || Response->Type != EJson::Object)
23
+ {
24
+ return nullptr;
25
+ }
26
+ return Response->AsObject();
27
+ }
28
+
29
+ TSharedPtr<FJsonObject> MCPQueryTestPredicate(const FString& Field, const FString& Op)
30
+ {
31
+ TSharedPtr<FJsonObject> Predicate = MakeShared<FJsonObject>();
32
+ Predicate->SetStringField(TEXT("field"), Field);
33
+ Predicate->SetStringField(TEXT("op"), Op);
34
+ return Predicate;
35
+ }
36
+ }
37
+
38
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
39
+ FLevelQueryComponentsContractTest,
40
+ "UE.MCP.Level.QueryComponents.Contract",
41
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
42
+
43
+ bool FLevelQueryComponentsContractTest::RunTest(const FString& Parameters)
44
+ {
45
+ FMCPHandlerRegistry Registry;
46
+ FLevelHandlers::RegisterHandlers(Registry);
47
+
48
+ TestTrue(TEXT("query_components is registered"), Registry.HasHandler(TEXT("query_components")));
49
+ TestTrue(
50
+ TEXT("query_components carries its own timeout, because a whole-map scan outlives the default"),
51
+ Registry.GetHandlerTimeout(TEXT("query_components")) > 0.0f);
52
+
53
+ // An unknown field group is an error rather than a silently empty
54
+ // projection. A projection that quietly produces nothing is how a caller
55
+ // ends up trusting a confident zero.
56
+ {
57
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
58
+ Request->SetArrayField(TEXT("fields"), { MakeShared<FJsonValueString>(TEXT("nonsense")) });
59
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
60
+ TestTrue(TEXT("unknown field group returns an object"), Response.IsValid());
61
+ if (Response.IsValid())
62
+ {
63
+ TestFalse(TEXT("unknown field group fails"), Response->GetBoolField(TEXT("success")));
64
+ TestTrue(
65
+ TEXT("unknown field group names the offending entry"),
66
+ Response->GetStringField(TEXT("error")).Contains(TEXT("nonsense")));
67
+ }
68
+ }
69
+
70
+ // An unknown operator is likewise rejected, and the message lists the
71
+ // operators that do exist.
72
+ {
73
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
74
+ Request->SetArrayField(TEXT("where"), {
75
+ MakeShared<FJsonValueObject>(MCPQueryTestPredicate(TEXT("shadow.castShadow"), TEXT("approximately"))) });
76
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
77
+ TestTrue(TEXT("unknown operator returns an object"), Response.IsValid());
78
+ if (Response.IsValid())
79
+ {
80
+ TestFalse(TEXT("unknown operator fails"), Response->GetBoolField(TEXT("success")));
81
+ TestTrue(
82
+ TEXT("unknown operator lists the valid ones"),
83
+ Response->GetStringField(TEXT("error")).Contains(TEXT("isTrue")));
84
+ }
85
+ }
86
+
87
+ // A predicate missing its field is rejected by index, so a caller with
88
+ // twenty predicates knows which one.
89
+ {
90
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
91
+ Request->SetArrayField(TEXT("where"), { MakeShared<FJsonValueObject>(MakeShared<FJsonObject>()) });
92
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
93
+ if (Response.IsValid())
94
+ {
95
+ TestFalse(TEXT("predicate without a field fails"), Response->GetBoolField(TEXT("success")));
96
+ TestTrue(
97
+ TEXT("predicate rejection names the index"),
98
+ Response->GetStringField(TEXT("error")).Contains(TEXT("where[0]")));
99
+ }
100
+ }
101
+
102
+ // suspectOnly is an AND, so combining it with whereMode 'any' would change
103
+ // what the other predicates mean. That is refused rather than guessed at.
104
+ {
105
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
106
+ Request->SetBoolField(TEXT("suspectOnly"), true);
107
+ Request->SetStringField(TEXT("whereMode"), TEXT("any"));
108
+ Request->SetArrayField(TEXT("where"), {
109
+ MakeShared<FJsonValueObject>(MCPQueryTestPredicate(TEXT("shadow.castShadow"), TEXT("isTrue"))) });
110
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
111
+ if (Response.IsValid())
112
+ {
113
+ TestFalse(TEXT("suspectOnly with whereMode any fails"), Response->GetBoolField(TEXT("success")));
114
+ }
115
+ }
116
+
117
+ // A levelPath that is not a map is refused before anything is opened.
118
+ {
119
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
120
+ Request->SetStringField(TEXT("levelPath"), TEXT("/Game/UEMCPDefinitelyNotAMap"));
121
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
122
+ if (Response.IsValid())
123
+ {
124
+ TestFalse(TEXT("a missing level package fails"), Response->GetBoolField(TEXT("success")));
125
+ }
126
+ }
127
+
128
+ return true;
129
+ }
130
+
131
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
132
+ FLevelQueryComponentsBoundsTest,
133
+ "UE.MCP.Level.QueryComponents.BoundedAndReadOnly",
134
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
135
+
136
+ bool FLevelQueryComponentsBoundsTest::RunTest(const FString& Parameters)
137
+ {
138
+ FMCPHandlerRegistry Registry;
139
+ FLevelHandlers::RegisterHandlers(Registry);
140
+
141
+ // An unfiltered query against whatever map is open. This is the shape that
142
+ // used to return over a megabyte, so the assertions are about the caps.
143
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
144
+ Request->SetNumberField(TEXT("limit"), 5);
145
+ Request->SetArrayField(TEXT("fields"), {
146
+ MakeShared<FJsonValueString>(TEXT("shadow")),
147
+ MakeShared<FJsonValueString>(TEXT("navigation")) });
148
+
149
+ const TSharedPtr<FJsonObject> Response = MCPQueryTestRun(Registry, Request);
150
+ if (!Response.IsValid() || !Response->GetBoolField(TEXT("success")))
151
+ {
152
+ // No editor world (a cook or commandlet context) is not a failure of
153
+ // this handler, and this test is not allowed to open a map to create
154
+ // one. Everything above already ran without a world.
155
+ return true;
156
+ }
157
+
158
+ const TArray<TSharedPtr<FJsonValue>>& Rows = Response->GetArrayField(TEXT("rows"));
159
+ TestTrue(TEXT("the row array honours 'limit'"), Rows.Num() <= 5);
160
+ TestTrue(
161
+ TEXT("'matched' is reported independently of how many rows came back"),
162
+ Response->GetNumberField(TEXT("matched")) >= Rows.Num());
163
+
164
+ // The #912 guarantee: a map-wide read leaves nothing dirty.
165
+ TestFalse(TEXT("a query dirties no packages"), Response->GetBoolField(TEXT("dirtiedPackages")));
166
+
167
+ // The #943 trap: a clean enum name, never EComponentMobility.MOVABLE.
168
+ for (const TSharedPtr<FJsonValue>& RowValue : Rows)
169
+ {
170
+ if (!RowValue.IsValid() || RowValue->Type != EJson::Object) continue;
171
+ const TSharedPtr<FJsonObject> Row = RowValue->AsObject();
172
+ if (!Row->HasTypedField<EJson::Object>(TEXT("navigation"))) continue;
173
+ const FString Mobility = Row->GetObjectField(TEXT("navigation"))->GetStringField(TEXT("mobility"));
174
+ TestFalse(
175
+ FString::Printf(TEXT("mobility '%s' carries no enum type prefix"), *Mobility),
176
+ Mobility.Contains(TEXT("EComponentMobility")));
177
+ }
178
+
179
+ // countOnly answers the aggregate question without paying for rows.
180
+ {
181
+ TSharedPtr<FJsonObject> CountRequest = MakeShared<FJsonObject>();
182
+ CountRequest->SetBoolField(TEXT("countOnly"), true);
183
+ CountRequest->SetStringField(TEXT("groupBy"), TEXT("actorClass"));
184
+ const TSharedPtr<FJsonObject> CountResponse = MCPQueryTestRun(Registry, CountRequest);
185
+ if (CountResponse.IsValid() && CountResponse->GetBoolField(TEXT("success")))
186
+ {
187
+ TestFalse(TEXT("countOnly returns no rows array"), CountResponse->HasField(TEXT("rows")));
188
+ TestTrue(TEXT("countOnly still groups"), CountResponse->HasField(TEXT("groups")));
189
+ }
190
+ }
191
+
192
+ return true;
193
+ }
194
+
195
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -0,0 +1,142 @@
1
+ // Coverage for the level refresh and inspect actions (#944, #915, #914).
2
+ //
3
+ // run_automation_tests dispatches every EditorContext/EngineFilter test in the
4
+ // process against whatever project the bridge is attached to. Two of these
5
+ // three actions rebuild engine state, so the assertions stay on the paths
6
+ // specified to change nothing: the refusals that keep them from running
7
+ // unbounded, and the dry run.
8
+
9
+ #if WITH_DEV_AUTOMATION_TESTS
10
+
11
+ #include "HandlerRegistry.h"
12
+ #include "Handlers/LevelHandlers.h"
13
+ #include "Misc/AutomationTest.h"
14
+
15
+ namespace
16
+ {
17
+ TSharedPtr<FJsonObject> MCPRefreshTestRun(
18
+ FMCPHandlerRegistry& Registry,
19
+ const TCHAR* Method,
20
+ const TSharedPtr<FJsonObject>& Request)
21
+ {
22
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
23
+ if (!Response.IsValid() || Response->Type != EJson::Object)
24
+ {
25
+ return nullptr;
26
+ }
27
+ return Response->AsObject();
28
+ }
29
+ }
30
+
31
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
32
+ FLevelRefreshRefusesUnboundedTest,
33
+ "UE.MCP.Level.Refresh.RefusesAnUnboundedRequest",
34
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
35
+
36
+ bool FLevelRefreshRefusesUnboundedTest::RunTest(const FString& Parameters)
37
+ {
38
+ FMCPHandlerRegistry Registry;
39
+ FLevelHandlers::RegisterHandlers(Registry);
40
+
41
+ for (const TCHAR* Method : {
42
+ TEXT("rerun_construction_scripts"),
43
+ TEXT("recreate_physics_state"),
44
+ TEXT("test_component_overlap") })
45
+ {
46
+ TestTrue(FString::Printf(TEXT("%s is registered"), Method), Registry.HasHandler(Method));
47
+ }
48
+
49
+ // Rerunning every construction script in a map destroys and rebuilds every
50
+ // generated component in it, so an empty request is refused rather than
51
+ // treated as "all".
52
+ {
53
+ const TSharedPtr<FJsonObject> Response =
54
+ MCPRefreshTestRun(Registry, TEXT("rerun_construction_scripts"), MakeShared<FJsonObject>());
55
+ if (Response.IsValid())
56
+ {
57
+ TestFalse(TEXT("an unbounded rerun fails"), Response->GetBoolField(TEXT("success")));
58
+ TestTrue(TEXT("and says which selectors it wants"),
59
+ Response->GetStringField(TEXT("error")).Contains(TEXT("actorLabels")));
60
+ }
61
+ }
62
+
63
+ // Same for the physics rebuild, which is long and disruptive and whose bug
64
+ // is always localised to the meshes that changed.
65
+ {
66
+ const TSharedPtr<FJsonObject> Response =
67
+ MCPRefreshTestRun(Registry, TEXT("recreate_physics_state"), MakeShared<FJsonObject>());
68
+ if (Response.IsValid())
69
+ {
70
+ TestFalse(TEXT("an unbounded physics rebuild fails"), Response->GetBoolField(TEXT("success")));
71
+ TestTrue(TEXT("and says so explicitly"),
72
+ Response->GetStringField(TEXT("error")).Contains(TEXT("whole world")));
73
+ }
74
+ }
75
+
76
+ // An unknown overlap method is named rather than silently treated as OBB.
77
+ {
78
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
79
+ Request->SetStringField(TEXT("actorLabelA"), TEXT("A"));
80
+ Request->SetStringField(TEXT("actorLabelB"), TEXT("B"));
81
+ Request->SetStringField(TEXT("method"), TEXT("Sphere"));
82
+ const TSharedPtr<FJsonObject> Response =
83
+ MCPRefreshTestRun(Registry, TEXT("test_component_overlap"), Request);
84
+ if (Response.IsValid())
85
+ {
86
+ TestFalse(TEXT("an unknown method fails"), Response->GetBoolField(TEXT("success")));
87
+ TestTrue(TEXT("and lists the ones that exist"),
88
+ Response->GetStringField(TEXT("error")).Contains(TEXT("AABB")));
89
+ }
90
+ }
91
+
92
+ // Both labels are required, and a missing one is named.
93
+ {
94
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
95
+ Request->SetStringField(TEXT("actorLabelA"), TEXT("A"));
96
+ const TSharedPtr<FJsonObject> Response =
97
+ MCPRefreshTestRun(Registry, TEXT("test_component_overlap"), Request);
98
+ if (Response.IsValid())
99
+ {
100
+ TestFalse(TEXT("a one-sided overlap test fails"), Response->GetBoolField(TEXT("success")));
101
+ TestTrue(TEXT("and names the missing parameter"),
102
+ Response->GetStringField(TEXT("error")).Contains(TEXT("actorLabelB")));
103
+ }
104
+ }
105
+
106
+ return true;
107
+ }
108
+
109
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
110
+ FLevelRecreatePhysicsPreviewTest,
111
+ "UE.MCP.Level.Refresh.PhysicsRebuildPreviewsByDefault",
112
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
113
+
114
+ bool FLevelRecreatePhysicsPreviewTest::RunTest(const FString& Parameters)
115
+ {
116
+ FMCPHandlerRegistry Registry;
117
+ FLevelHandlers::RegisterHandlers(Registry);
118
+
119
+ // A bounded request with no dryRun given. The preflight is the default,
120
+ // which is what #915 asked for: see what would be rebuilt, and what its
121
+ // collision state is now, before rebuilding it.
122
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
123
+ Request->SetStringField(TEXT("componentClass"), TEXT("StaticMeshComponent"));
124
+
125
+ const TSharedPtr<FJsonObject> Response =
126
+ MCPRefreshTestRun(Registry, TEXT("recreate_physics_state"), Request);
127
+ if (!Response.IsValid() || !Response->GetBoolField(TEXT("success")))
128
+ {
129
+ // No editor world in this context, and this test may not open a map.
130
+ return true;
131
+ }
132
+
133
+ TestTrue(TEXT("dryRun defaults to true"), Response->GetBoolField(TEXT("dryRun")));
134
+ TestTrue(TEXT("a preview reports what it would rebuild"), Response->HasField(TEXT("wouldRecreate")));
135
+ TestFalse(TEXT("and does not claim to have rebuilt anything"), Response->HasField(TEXT("recreated")));
136
+ TestTrue(TEXT("a preview still returns the current collision summary"),
137
+ Response->HasField(TEXT("collisionSummary")));
138
+
139
+ return true;
140
+ }
141
+
142
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -0,0 +1,125 @@
1
+ // Coverage for level(save) reporting the truth about the packages it touched
2
+ // (#964).
3
+ //
4
+ // The failure this guards is a FALSE failure: the old action answered
5
+ // {"success": false, "error": "Failed to save current level"} on a map that
6
+ // editor(save_dirty) then wrote seconds later. An agent that believes a save
7
+ // did not happen redoes work that was already on disk, so a save reporting
8
+ // failure when nothing failed is more expensive than one that reports nothing.
9
+ //
10
+ // The test deliberately writes nothing. It runs only when the level and its
11
+ // external packages are already clean, and then asserts that a save with
12
+ // nothing to do is reported as the success it is, names the package it
13
+ // considered, and carries no error. A test that saved the map to check saving
14
+ // would leave exactly the kind of unasked-for commit this repo works to avoid.
15
+
16
+ #if WITH_DEV_AUTOMATION_TESTS
17
+
18
+ #include "Editor.h"
19
+ #include "Engine/Level.h"
20
+ #include "Engine/World.h"
21
+ #include "HandlerRegistry.h"
22
+ #include "Handlers/LevelHandlers.h"
23
+ #include "Misc/AutomationTest.h"
24
+ #include "UObject/Package.h"
25
+
26
+ namespace
27
+ {
28
+ TSharedPtr<FJsonObject> MCPLevelSaveTestRun(
29
+ FMCPHandlerRegistry& Registry,
30
+ const TSharedPtr<FJsonObject>& Request)
31
+ {
32
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("save_level"), Request);
33
+ if (!Response.IsValid() || Response->Type != EJson::Object)
34
+ {
35
+ return nullptr;
36
+ }
37
+ return Response->AsObject();
38
+ }
39
+
40
+ /** True when saving would write nothing at all, so the test is safe to run. */
41
+ bool MCPLevelSaveTestNothingIsDirty(UWorld* World)
42
+ {
43
+ if (!World) return false;
44
+ ULevel* Level = World->GetCurrentLevel();
45
+ if (!Level) Level = World->PersistentLevel;
46
+ if (!Level) return false;
47
+ UPackage* Package = Level->GetOutermost();
48
+ if (!Package || Package->IsDirty()) return false;
49
+ if (Level->IsUsingExternalObjects())
50
+ {
51
+ for (UPackage* External : Level->GetLoadedExternalObjectPackages())
52
+ {
53
+ if (External && External->IsDirty()) return false;
54
+ }
55
+ }
56
+ return true;
57
+ }
58
+ }
59
+
60
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
61
+ FLevelSaveReportsWhatHappenedTest,
62
+ "UE.MCP.Level.Save.ReportsWhatHappenedToEachPackage",
63
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
64
+
65
+ bool FLevelSaveReportsWhatHappenedTest::RunTest(const FString& Parameters)
66
+ {
67
+ FMCPHandlerRegistry Registry;
68
+ FLevelHandlers::RegisterHandlers(Registry);
69
+ TestTrue(TEXT("save_level is registered"), Registry.HasHandler(TEXT("save_level")));
70
+
71
+ UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
72
+ if (!World) return true;
73
+ if (GEditor->PlayWorld != nullptr || GEditor->bIsSimulatingInEditor) return true;
74
+ if (!MCPLevelSaveTestNothingIsDirty(World))
75
+ {
76
+ // Something is unsaved. Running the action here would write it, and
77
+ // this test is not permitted to commit anybody's work for them.
78
+ AddInfo(TEXT("Skipped: the open level has unsaved changes, and this test never writes."));
79
+ return true;
80
+ }
81
+
82
+ const TSharedPtr<FJsonObject> Response = MCPLevelSaveTestRun(Registry, MakeShared<FJsonObject>());
83
+ if (!Response.IsValid()) return true;
84
+
85
+ // The regression: nothing failed, so nothing may be reported as a failure.
86
+ TestTrue(TEXT("a save with nothing to do succeeds"), Response->GetBoolField(TEXT("success")));
87
+ FString Error;
88
+ TestFalse(TEXT("and carries no error"), Response->TryGetStringField(TEXT("error"), Error));
89
+ TestEqual(TEXT("and reports that nothing failed"),
90
+ static_cast<int32>(Response->GetNumberField(TEXT("failedCount"))), 0);
91
+ TestEqual(TEXT("and that nothing needed writing"),
92
+ static_cast<int32>(Response->GetNumberField(TEXT("savedCount"))), 0);
93
+
94
+ // The diagnostic half: the response names what it considered, so a caller
95
+ // can tell "already saved" from "saved nothing because it looked nowhere".
96
+ FString LevelPackage;
97
+ TestTrue(TEXT("the response names the level package"),
98
+ Response->TryGetStringField(TEXT("levelPackage"), LevelPackage));
99
+ TestTrue(TEXT("which is not empty"), !LevelPackage.IsEmpty());
100
+ FString Message;
101
+ TestTrue(TEXT("and says in words that nothing needed writing"),
102
+ Response->TryGetStringField(TEXT("message"), Message));
103
+
104
+ // A clean package appears as skipped with its reason, never as a failure.
105
+ const TArray<TSharedPtr<FJsonValue>>* SkippedRows = nullptr;
106
+ if (Response->TryGetArrayField(TEXT("skipped"), SkippedRows) && SkippedRows)
107
+ {
108
+ TestTrue(TEXT("the clean level package is listed as skipped"), SkippedRows->Num() >= 1);
109
+ if (SkippedRows->Num() > 0)
110
+ {
111
+ const TSharedPtr<FJsonObject> Row = (*SkippedRows)[0]->AsObject();
112
+ if (Row.IsValid())
113
+ {
114
+ TestFalse(TEXT("a skipped package is not marked saved"), Row->GetBoolField(TEXT("saved")));
115
+ TestFalse(TEXT("and was not dirty"), Row->GetBoolField(TEXT("wasDirty")));
116
+ FString RowError;
117
+ TestFalse(TEXT("and is not an error"), Row->TryGetStringField(TEXT("error"), RowError));
118
+ }
119
+ }
120
+ }
121
+
122
+ return true;
123
+ }
124
+
125
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -0,0 +1,42 @@
1
+ #if WITH_DEV_AUTOMATION_TESTS
2
+
3
+ #include "Handlers/LevelHandlers.h"
4
+ #include "EdGraph/EdGraph.h"
5
+ #include "EdGraph/EdGraphNode.h"
6
+ #include "Engine/Blueprint.h"
7
+ #include "EdGraphSchema_K2.h"
8
+ #include "Kismet2/BlueprintEditorUtils.h"
9
+ #include "Misc/AutomationTest.h"
10
+
11
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
12
+ FLevelScriptClearGraphNodesTest,
13
+ "UE.MCP.Level.ClearLevelScript.GraphNodes",
14
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
15
+
16
+ bool FLevelScriptClearGraphNodesTest::RunTest(const FString& Parameters)
17
+ {
18
+ UBlueprint* Blueprint = NewObject<UBlueprint>(GetTransientPackage());
19
+ UEdGraph* Graph = NewObject<UEdGraph>(Blueprint, TEXT("EventGraph"));
20
+ Blueprint->UbergraphPages.Add(Graph);
21
+ Graph->AddNode(NewObject<UEdGraphNode>(Graph, TEXT("FirstNode")));
22
+ Graph->AddNode(NewObject<UEdGraphNode>(Graph, TEXT("SecondNode")));
23
+ FEdGraphPinType VariableType;
24
+ VariableType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
25
+ FBlueprintEditorUtils::AddMemberVariable(Blueprint, TEXT("LegacyReference"), VariableType);
26
+
27
+ TArray<TSharedPtr<FJsonValue>> Graphs;
28
+ TestEqual(TEXT("dry run reports both nodes"),
29
+ FLevelHandlers::ClearBlueprintGraphNodes(Blueprint, true, Graphs), 2);
30
+ TestEqual(TEXT("dry run keeps both nodes"), Graph->Nodes.Num(), 2);
31
+ TestEqual(TEXT("dry run reports the graph"), Graphs.Num(), 1);
32
+
33
+ Graphs.Reset();
34
+ TestEqual(TEXT("apply removes both nodes"),
35
+ FLevelHandlers::ClearBlueprintGraphNodes(Blueprint, false, Graphs), 2);
36
+ TestEqual(TEXT("apply leaves the graph empty"), Graph->Nodes.Num(), 0);
37
+ FBlueprintEditorUtils::RemoveMemberVariable(Blueprint, TEXT("LegacyReference"));
38
+ TestEqual(TEXT("member variable can be removed"), Blueprint->NewVariables.Num(), 0);
39
+ return true;
40
+ }
41
+
42
+ #endif