ue-mcp 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +4 -3
  2. package/dist/action-class.js +56 -3
  3. package/dist/action-class.js.map +1 -1
  4. package/dist/bridge-timeouts.d.ts +64 -0
  5. package/dist/bridge-timeouts.js +118 -0
  6. package/dist/bridge-timeouts.js.map +1 -0
  7. package/dist/bridge.js +10 -2
  8. package/dist/bridge.js.map +1 -1
  9. package/dist/editor-control.d.ts +72 -5
  10. package/dist/editor-control.js +164 -140
  11. package/dist/editor-control.js.map +1 -1
  12. package/dist/editor-target.d.ts +55 -1
  13. package/dist/editor-target.js +107 -10
  14. package/dist/editor-target.js.map +1 -1
  15. package/dist/engine-observer.d.ts +59 -5
  16. package/dist/engine-observer.js +249 -29
  17. package/dist/engine-observer.js.map +1 -1
  18. package/dist/engine-root.d.ts +91 -0
  19. package/dist/engine-root.js +448 -0
  20. package/dist/engine-root.js.map +1 -0
  21. package/dist/epic-enrich.js +16 -1
  22. package/dist/epic-enrich.js.map +1 -1
  23. package/dist/feedback-cli.js +4 -0
  24. package/dist/feedback-cli.js.map +1 -1
  25. package/dist/feedback-deferred.d.ts +9 -0
  26. package/dist/feedback-deferred.js +2 -0
  27. package/dist/feedback-deferred.js.map +1 -1
  28. package/dist/feedback-fallback.d.ts +99 -0
  29. package/dist/feedback-fallback.js +173 -0
  30. package/dist/feedback-fallback.js.map +1 -0
  31. package/dist/flow/write-methods.js +20 -0
  32. package/dist/flow/write-methods.js.map +1 -1
  33. package/dist/lean-context.js +8 -3
  34. package/dist/lean-context.js.map +1 -1
  35. package/dist/locking.js +11 -2
  36. package/dist/locking.js.map +1 -1
  37. package/dist/project.d.ts +11 -0
  38. package/dist/project.js +23 -2
  39. package/dist/project.js.map +1 -1
  40. package/dist/python-gate.d.ts +95 -0
  41. package/dist/python-gate.js +175 -0
  42. package/dist/python-gate.js.map +1 -0
  43. package/dist/tool-counts.json +16 -16
  44. package/dist/tools/animation.js +288 -24
  45. package/dist/tools/animation.js.map +1 -1
  46. package/dist/tools/asset.js +79 -14
  47. package/dist/tools/asset.js.map +1 -1
  48. package/dist/tools/audio.js +59 -5
  49. package/dist/tools/audio.js.map +1 -1
  50. package/dist/tools/blueprint.js +29 -12
  51. package/dist/tools/blueprint.js.map +1 -1
  52. package/dist/tools/chooser.js +4 -1
  53. package/dist/tools/chooser.js.map +1 -1
  54. package/dist/tools/editor.js +81 -43
  55. package/dist/tools/editor.js.map +1 -1
  56. package/dist/tools/feedback.js +214 -48
  57. package/dist/tools/feedback.js.map +1 -1
  58. package/dist/tools/foliage.js +27 -1
  59. package/dist/tools/foliage.js.map +1 -1
  60. package/dist/tools/gameplay.js +38 -11
  61. package/dist/tools/gameplay.js.map +1 -1
  62. package/dist/tools/gas.js +17 -5
  63. package/dist/tools/gas.js.map +1 -1
  64. package/dist/tools/landscape.js +22 -7
  65. package/dist/tools/landscape.js.map +1 -1
  66. package/dist/tools/level.js +322 -51
  67. package/dist/tools/level.js.map +1 -1
  68. package/dist/tools/material.js +10 -4
  69. package/dist/tools/material.js.map +1 -1
  70. package/dist/tools/niagara.js +3 -2
  71. package/dist/tools/niagara.js.map +1 -1
  72. package/dist/tools/pcg.js +6 -5
  73. package/dist/tools/pcg.js.map +1 -1
  74. package/dist/tools/project.js +47 -20
  75. package/dist/tools/project.js.map +1 -1
  76. package/dist/types.d.ts +21 -0
  77. package/dist/types.js +46 -3
  78. package/dist/types.js.map +1 -1
  79. package/dist/ue-mcp.default.yml +493 -125
  80. package/package.json +2 -2
  81. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  82. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  83. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  84. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  85. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  86. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  87. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  88. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  89. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  90. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  91. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  92. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  93. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  94. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  95. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  242. package/skills/ue-mcp-animation/SKILL.md +154 -0
  243. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -0,0 +1,362 @@
1
+ // Engine-side coverage for the Blueprint category surface that is safe to run
2
+ // anywhere.
3
+ //
4
+ // run_automation_tests dispatches every EditorContext/EngineFilter test in the
5
+ // process when it is called without a filter, against whatever project the
6
+ // bridge is attached to. Nothing below creates, compiles, saves or deletes an
7
+ // asset: every case is registration, argument validation, or a question asked
8
+ // of the engine's own reflection data. The behaviour that needs real assets is
9
+ // covered by the smoke suite against the dedicated test project instead.
10
+
11
+ #if WITH_DEV_AUTOMATION_TESTS
12
+
13
+ #include "HandlerRegistry.h"
14
+ #include "Handlers/BlueprintHandlers.h"
15
+ #include "Misc/AutomationTest.h"
16
+ #include "Dom/JsonObject.h"
17
+ #include "Dom/JsonValue.h"
18
+ #include "EdGraphSchema_K2.h"
19
+ #include "BehaviorTree/Tasks/BTTask_BlueprintBase.h"
20
+ #include "UObject/UnrealType.h"
21
+
22
+ namespace
23
+ {
24
+ TSharedPtr<FJsonObject> BlueprintTestResponseObject(const TSharedPtr<FJsonValue>& Response)
25
+ {
26
+ return (Response.IsValid() && Response->Type == EJson::Object) ? Response->AsObject() : nullptr;
27
+ }
28
+ }
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // #942: a World/umap path now resolves to that map's level script Blueprint.
32
+ // The half of that contract which needs no assets is the other half: a path
33
+ // that is neither a Blueprint nor a World must still report the plain
34
+ // "Blueprint not found" message, so the level-script hint cannot leak into an
35
+ // ordinary typo and send a caller looking for a map that was never named.
36
+ // ---------------------------------------------------------------------------
37
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
38
+ FBlueprintNotFoundReportingTest,
39
+ "UE.MCP.Blueprint.Surface.NotFoundReporting",
40
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
41
+
42
+ bool FBlueprintNotFoundReportingTest::RunTest(const FString& Parameters)
43
+ {
44
+ FMCPHandlerRegistry Registry;
45
+ FBlueprintHandlers::RegisterHandlers(Registry);
46
+
47
+ // Every action named in #942 shares one lookup, so they share one message.
48
+ const TCHAR* SharedLookupActions[] = {
49
+ TEXT("read_blueprint"),
50
+ TEXT("list_blueprint_graphs"),
51
+ TEXT("read_blueprint_graph"),
52
+ TEXT("get_blueprint_execution_flow"),
53
+ };
54
+
55
+ for (const TCHAR* Action : SharedLookupActions)
56
+ {
57
+ TestTrue(*FString::Printf(TEXT("%s is registered"), Action), Registry.HasHandler(Action));
58
+
59
+ TSharedPtr<FJsonObject> BadPath = MakeShared<FJsonObject>();
60
+ BadPath->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCPTests/BP_ThisAssetDoesNotExist"));
61
+ BadPath->SetStringField(TEXT("graphName"), TEXT("EventGraph"));
62
+
63
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
64
+ Registry.ExecuteHandler(Action, BadPath));
65
+ if (!TestTrue(*FString::Printf(TEXT("%s returns an object"), Action), Response.IsValid()))
66
+ {
67
+ continue;
68
+ }
69
+ TestFalse(*FString::Printf(TEXT("%s rejects an unknown path"), Action),
70
+ Response->GetBoolField(TEXT("success")));
71
+ TestTrue(*FString::Printf(TEXT("%s reports a missing Blueprint, not a missing level script"), Action),
72
+ Response->GetStringField(TEXT("error")).Contains(TEXT("Blueprint not found")));
73
+ }
74
+
75
+ // The lookup is also what rejects a request with no path at all, and that
76
+ // has to stay a parameter error rather than becoming the World hint.
77
+ {
78
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
79
+ Registry.ExecuteHandler(TEXT("list_blueprint_graphs"), MakeShared<FJsonObject>()));
80
+ if (TestTrue(TEXT("missing path returns an object"), Response.IsValid()))
81
+ {
82
+ TestFalse(TEXT("missing path is unsuccessful"), Response->GetBoolField(TEXT("success")));
83
+ TestFalse(TEXT("missing path is not reported as a level script miss"),
84
+ Response->GetStringField(TEXT("error")).Contains(TEXT("PersistentLevel")));
85
+ }
86
+ }
87
+
88
+ return true;
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // #902: get_variable_default has to be reachable by name and has to reject a
93
+ // malformed request rather than throw. A handler that fails to register reports
94
+ // "Unknown method" at runtime with a clean build behind it, which is the
95
+ // failure mode this asserts away. The value path itself needs a real compiled
96
+ // Blueprint and is covered by the smoke suite.
97
+ // ---------------------------------------------------------------------------
98
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
99
+ FBlueprintVariableDefaultRegistrationTest,
100
+ "UE.MCP.Blueprint.VariableDefault.RegistrationAndValidation",
101
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
102
+
103
+ bool FBlueprintVariableDefaultRegistrationTest::RunTest(const FString& Parameters)
104
+ {
105
+ FMCPHandlerRegistry Registry;
106
+ FBlueprintHandlers::RegisterHandlers(Registry);
107
+
108
+ TestTrue(TEXT("get_blueprint_variable_default is registered"),
109
+ Registry.HasHandler(TEXT("get_blueprint_variable_default")));
110
+
111
+ // assetPath is required.
112
+ {
113
+ TSharedPtr<FJsonObject> MissingPath = MakeShared<FJsonObject>();
114
+ MissingPath->SetStringField(TEXT("name"), TEXT("Health"));
115
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
116
+ Registry.ExecuteHandler(TEXT("get_blueprint_variable_default"), MissingPath));
117
+ if (TestTrue(TEXT("missing assetPath returns an object"), Response.IsValid()))
118
+ {
119
+ TestFalse(TEXT("missing assetPath is unsuccessful"), Response->GetBoolField(TEXT("success")));
120
+ }
121
+ }
122
+
123
+ // name is required, and is checked before the asset is touched so a caller
124
+ // that forgot it is told which field it forgot.
125
+ {
126
+ TSharedPtr<FJsonObject> MissingName = MakeShared<FJsonObject>();
127
+ MissingName->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCPTests/BP_ThisAssetDoesNotExist"));
128
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
129
+ Registry.ExecuteHandler(TEXT("get_blueprint_variable_default"), MissingName));
130
+ if (TestTrue(TEXT("missing name returns an object"), Response.IsValid()))
131
+ {
132
+ TestFalse(TEXT("missing name is unsuccessful"), Response->GetBoolField(TEXT("success")));
133
+ TestTrue(TEXT("missing name identifies the field"),
134
+ Response->GetStringField(TEXT("error")).Contains(TEXT("name")));
135
+ }
136
+ }
137
+
138
+ // An unknown asset reports the shared Blueprint lookup failure, not a
139
+ // property failure: the two are different problems with different fixes.
140
+ {
141
+ TSharedPtr<FJsonObject> UnknownAsset = MakeShared<FJsonObject>();
142
+ UnknownAsset->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCPTests/BP_ThisAssetDoesNotExist"));
143
+ UnknownAsset->SetStringField(TEXT("name"), TEXT("Health"));
144
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
145
+ Registry.ExecuteHandler(TEXT("get_blueprint_variable_default"), UnknownAsset));
146
+ if (TestTrue(TEXT("unknown asset returns an object"), Response.IsValid()))
147
+ {
148
+ TestFalse(TEXT("unknown asset is unsuccessful"), Response->GetBoolField(TEXT("success")));
149
+ TestTrue(TEXT("unknown asset reports a missing Blueprint"),
150
+ Response->GetStringField(TEXT("error")).Contains(TEXT("Blueprint not found")));
151
+ }
152
+ }
153
+
154
+ // list_variables keeps its default payload: includeValues is opt-in, so a
155
+ // request without it must not start resolving CDO values.
156
+ {
157
+ TSharedPtr<FJsonObject> MissingPath = MakeShared<FJsonObject>();
158
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
159
+ Registry.ExecuteHandler(TEXT("list_blueprint_variables"), MissingPath));
160
+ if (TestTrue(TEXT("list_variables with no path returns an object"), Response.IsValid()))
161
+ {
162
+ TestFalse(TEXT("list_variables with no path is unsuccessful"),
163
+ Response->GetBoolField(TEXT("success")));
164
+ }
165
+ }
166
+
167
+ return true;
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // #886: authoring a BTTask Blueprint's event graph needs ReceiveExecuteAI and
172
+ // ReceiveAbortAI as override events and FinishExecute / FinishAbort as call
173
+ // nodes. The existing override_function and add_node surface already places
174
+ // all four; what makes that true is the shape those two actions test for on
175
+ // the engine side, so that shape is what is asserted here. If Epic ever
176
+ // changes one of these functions, the recipe the skill documents becomes wrong
177
+ // here first, in a test, rather than in somebody's build.
178
+ // ---------------------------------------------------------------------------
179
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
180
+ FBlueprintBTTaskEventShapeTest,
181
+ "UE.MCP.Blueprint.BTTask.EventShape",
182
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
183
+
184
+ bool FBlueprintBTTaskEventShapeTest::RunTest(const FString& Parameters)
185
+ {
186
+ UClass* TaskBase = UBTTask_BlueprintBase::StaticClass();
187
+ if (!TestNotNull(TEXT("UBTTask_BlueprintBase resolves"), TaskBase))
188
+ {
189
+ return false;
190
+ }
191
+
192
+ // override_function accepts a name only when GetOverrideFunctionClass can
193
+ // resolve it, which needs CanKismetOverrideFunction; it places an override
194
+ // EVENT rather than a function graph only when FunctionCanBePlacedAsEvent
195
+ // agrees. Both have to hold for the documented call sequence to work.
196
+ const TCHAR* OverrideEvents[] = {
197
+ TEXT("ReceiveExecuteAI"), TEXT("ReceiveAbortAI"), TEXT("ReceiveTickAI"),
198
+ TEXT("ReceiveExecute"), TEXT("ReceiveAbort"), TEXT("ReceiveTick"),
199
+ };
200
+ for (const TCHAR* EventName : OverrideEvents)
201
+ {
202
+ UFunction* Function = TaskBase->FindFunctionByName(FName(EventName));
203
+ if (!TestNotNull(*FString::Printf(TEXT("%s exists on UBTTask_BlueprintBase"), EventName), Function))
204
+ {
205
+ continue;
206
+ }
207
+ TestTrue(*FString::Printf(TEXT("%s is overridable from Kismet"), EventName),
208
+ UEdGraphSchema_K2::CanKismetOverrideFunction(Function));
209
+ TestTrue(*FString::Printf(TEXT("%s can be placed as an override event"), EventName),
210
+ UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(Function));
211
+ }
212
+
213
+ // add_node(nodeClass="CallFunction") binds a K2Node_CallFunction to a
214
+ // UFUNCTION resolved by name on the named class, and only a
215
+ // BlueprintCallable function yields a usable node.
216
+ const TCHAR* CallableFinishers[] = { TEXT("FinishExecute"), TEXT("FinishAbort") };
217
+ for (const TCHAR* CallName : CallableFinishers)
218
+ {
219
+ UFunction* Function = TaskBase->FindFunctionByName(FName(CallName));
220
+ if (!TestNotNull(*FString::Printf(TEXT("%s exists on UBTTask_BlueprintBase"), CallName), Function))
221
+ {
222
+ continue;
223
+ }
224
+ TestTrue(*FString::Printf(TEXT("%s is BlueprintCallable"), CallName),
225
+ Function->HasAnyFunctionFlags(FUNC_BlueprintCallable));
226
+ }
227
+
228
+ // The skill documents setting bSuccess as a pin default on FinishExecute,
229
+ // which only holds while the parameter is there and is a bool.
230
+ if (UFunction* FinishExecute = TaskBase->FindFunctionByName(TEXT("FinishExecute")))
231
+ {
232
+ FProperty* SuccessParam = FinishExecute->FindPropertyByName(TEXT("bSuccess"));
233
+ if (TestNotNull(TEXT("FinishExecute exposes bSuccess"), SuccessParam))
234
+ {
235
+ TestTrue(TEXT("FinishExecute's bSuccess is a bool"), SuccessParam->IsA<FBoolProperty>());
236
+ }
237
+ }
238
+
239
+ return true;
240
+ }
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // #945: search_call_sites runs against an unknown-sized project, so its
244
+ // argument validation is what stops a malformed request turning into a
245
+ // full-project package load. Everything asserted here rejects before a single
246
+ // asset is touched. The matching itself needs real Blueprints and belongs to
247
+ // the smoke suite.
248
+ // ---------------------------------------------------------------------------
249
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
250
+ FBlueprintSearchCallSitesValidationTest,
251
+ "UE.MCP.Blueprint.SearchCallSites.RegistrationAndValidation",
252
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
253
+
254
+ bool FBlueprintSearchCallSitesValidationTest::RunTest(const FString& Parameters)
255
+ {
256
+ FMCPHandlerRegistry Registry;
257
+ FBlueprintHandlers::RegisterHandlers(Registry);
258
+
259
+ TestTrue(TEXT("search_blueprint_call_sites is registered"),
260
+ Registry.HasHandler(TEXT("search_blueprint_call_sites")));
261
+ // A sweep that pays for package loads cannot finish inside the default
262
+ // request timeout, so it has to carry its own.
263
+ TestTrue(TEXT("search_blueprint_call_sites carries its own timeout"),
264
+ Registry.GetHandlerTimeout(TEXT("search_blueprint_call_sites")) > 0.0f);
265
+
266
+ // functionNames is required. Without it there is nothing to search for, and
267
+ // defaulting to "everything" would load the whole project.
268
+ {
269
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
270
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), MakeShared<FJsonObject>()));
271
+ if (TestTrue(TEXT("missing functionNames returns an object"), Response.IsValid()))
272
+ {
273
+ TestFalse(TEXT("missing functionNames is unsuccessful"), Response->GetBoolField(TEXT("success")));
274
+ TestTrue(TEXT("missing functionNames identifies the field"),
275
+ Response->GetStringField(TEXT("error")).Contains(TEXT("functionNames")));
276
+ }
277
+ }
278
+
279
+ // An empty array, and an array of nothing but blanks, are the same mistake.
280
+ {
281
+ TSharedPtr<FJsonObject> EmptyNames = MakeShared<FJsonObject>();
282
+ EmptyNames->SetArrayField(TEXT("functionNames"), TArray<TSharedPtr<FJsonValue>>());
283
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
284
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), EmptyNames));
285
+ if (TestTrue(TEXT("empty functionNames returns an object"), Response.IsValid()))
286
+ {
287
+ TestFalse(TEXT("empty functionNames is unsuccessful"), Response->GetBoolField(TEXT("success")));
288
+ }
289
+ }
290
+ {
291
+ TSharedPtr<FJsonObject> BlankNames = MakeShared<FJsonObject>();
292
+ TArray<TSharedPtr<FJsonValue>> Blanks;
293
+ Blanks.Add(MakeShared<FJsonValueString>(TEXT(" ")));
294
+ BlankNames->SetArrayField(TEXT("functionNames"), Blanks);
295
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
296
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), BlankNames));
297
+ if (TestTrue(TEXT("whitespace-only functionNames returns an object"), Response.IsValid()))
298
+ {
299
+ TestFalse(TEXT("whitespace-only functionNames is unsuccessful"),
300
+ Response->GetBoolField(TEXT("success")));
301
+ }
302
+ }
303
+
304
+ // The batch cap: over it, the caller is told to split rather than being
305
+ // handed a sweep that will not finish.
306
+ {
307
+ TSharedPtr<FJsonObject> TooMany = MakeShared<FJsonObject>();
308
+ TArray<TSharedPtr<FJsonValue>> Names;
309
+ for (int32 Index = 0; Index < 51; ++Index)
310
+ {
311
+ Names.Add(MakeShared<FJsonValueString>(FString::Printf(TEXT("Function_%d"), Index)));
312
+ }
313
+ TooMany->SetArrayField(TEXT("functionNames"), Names);
314
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
315
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), TooMany));
316
+ if (TestTrue(TEXT("oversized functionNames returns an object"), Response.IsValid()))
317
+ {
318
+ TestFalse(TEXT("oversized functionNames is unsuccessful"), Response->GetBoolField(TEXT("success")));
319
+ TestTrue(TEXT("oversized functionNames reports the bound"),
320
+ Response->GetStringField(TEXT("error")).Contains(TEXT("50")));
321
+ }
322
+ }
323
+
324
+ // An unresolvable className is a typo, and matching nothing would read as
325
+ // "no call sites" - the wrong answer to an audit.
326
+ {
327
+ TSharedPtr<FJsonObject> BadClass = MakeShared<FJsonObject>();
328
+ TArray<TSharedPtr<FJsonValue>> Names;
329
+ Names.Add(MakeShared<FJsonValueString>(TEXT("FinishExecute")));
330
+ BadClass->SetArrayField(TEXT("functionNames"), Names);
331
+ BadClass->SetStringField(TEXT("className"), TEXT("ThisClassDoesNotExistAnywhere_UEMCP"));
332
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
333
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), BadClass));
334
+ if (TestTrue(TEXT("unknown className returns an object"), Response.IsValid()))
335
+ {
336
+ TestFalse(TEXT("unknown className is unsuccessful"), Response->GetBoolField(TEXT("success")));
337
+ TestEqual(TEXT("unknown className is reported as a class miss"),
338
+ Response->GetStringField(TEXT("reason")), FString(TEXT("class_not_found")));
339
+ }
340
+ }
341
+
342
+ // A directory that is not mount-rooted would silently match nothing.
343
+ {
344
+ TSharedPtr<FJsonObject> BadDirectory = MakeShared<FJsonObject>();
345
+ TArray<TSharedPtr<FJsonValue>> Names;
346
+ Names.Add(MakeShared<FJsonValueString>(TEXT("FinishExecute")));
347
+ BadDirectory->SetArrayField(TEXT("functionNames"), Names);
348
+ BadDirectory->SetStringField(TEXT("directory"), TEXT("Content/AI"));
349
+ const TSharedPtr<FJsonObject> Response = BlueprintTestResponseObject(
350
+ Registry.ExecuteHandler(TEXT("search_blueprint_call_sites"), BadDirectory));
351
+ if (TestTrue(TEXT("relative directory returns an object"), Response.IsValid()))
352
+ {
353
+ TestFalse(TEXT("relative directory is unsuccessful"), Response->GetBoolField(TEXT("success")));
354
+ TestTrue(TEXT("relative directory says what a directory looks like"),
355
+ Response->GetStringField(TEXT("error")).Contains(TEXT("/Game")));
356
+ }
357
+ }
358
+
359
+ return true;
360
+ }
361
+
362
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -628,6 +628,20 @@ bool FMCPBridgeCapabilitiesTest::RunTest(const FString& Parameters)
628
628
  double ActionCount = 0.0;
629
629
  TestTrue(TEXT("the registered action count is reported"), Payload->TryGetNumberField(TEXT("actionCount"), ActionCount));
630
630
  TestTrue(TEXT("the binary registered some actions"), ActionCount > 0);
631
+ const TArray<TSharedPtr<FJsonValue>>* Actions = nullptr;
632
+ TestTrue(TEXT("the registered actions are reported"), Payload->TryGetArrayField(TEXT("actions"), Actions));
633
+ if (Actions)
634
+ {
635
+ TSet<FString> ActionNames;
636
+ for (const TSharedPtr<FJsonValue>& Value : *Actions)
637
+ {
638
+ ActionNames.Add(Value->AsString());
639
+ }
640
+ TestTrue(TEXT("runtime visibility set is advertised"),
641
+ ActionNames.Contains(TEXT("set_runtime_visibility")));
642
+ TestTrue(TEXT("runtime visibility restore is advertised"),
643
+ ActionNames.Contains(TEXT("restore_runtime_visibility")));
644
+ }
631
645
 
632
646
  // Advertised whether or not it is recording, so a caller can tell a bridge
633
647
  // that cannot from a bridge that is switched off.
@@ -0,0 +1,288 @@
1
+ // Coverage for #925.
2
+ //
3
+ // blueprint(read_component_properties) exports BodyInstance as text, and that
4
+ // text carries CollisionProfileName plus the ResponseArray of overrides that
5
+ // DIFFER from the profile. A response inherited from the profile is not in it,
6
+ // so "does this capsule block ECC_Camera" was unanswerable from the dump, and
7
+ // editor(get_object_properties) has the same limit.
8
+ //
9
+ // These assertions run against ACharacter's own capsule, which every engine
10
+ // ships with the Pawn profile, so they check the exact thing the issue asked
11
+ // for: an EFFECTIVE response that the component itself never overrides and that
12
+ // therefore only exists in the profile. Nothing is created, loaded from the
13
+ // attached project, or written.
14
+
15
+ #if WITH_DEV_AUTOMATION_TESTS
16
+
17
+ #include "HandlerRegistry.h"
18
+ #include "HandlerUtils.h"
19
+ #include "Handlers/BlueprintHandlers_Collision.h"
20
+
21
+ #include "Components/CapsuleComponent.h"
22
+ #include "Dom/JsonObject.h"
23
+ #include "Dom/JsonValue.h"
24
+ #include "Engine/CollisionProfile.h"
25
+ #include "GameFramework/Character.h"
26
+ #include "Misc/AutomationTest.h"
27
+
28
+ namespace
29
+ {
30
+ TSharedPtr<FJsonObject> CollisionResponseObject(const TSharedPtr<FJsonValue>& Response)
31
+ {
32
+ return (Response.IsValid() && Response->Type == EJson::Object)
33
+ ? Response->AsObject()
34
+ : TSharedPtr<FJsonObject>();
35
+ }
36
+
37
+ bool CollisionResponseBool(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field, bool bDefault = false)
38
+ {
39
+ const TSharedPtr<FJsonObject> Obj = CollisionResponseObject(Response);
40
+ if (!Obj.IsValid()) return bDefault;
41
+ bool bValue = bDefault;
42
+ Obj->TryGetBoolField(Field, bValue);
43
+ return bValue;
44
+ }
45
+
46
+ FString CollisionResponseString(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field)
47
+ {
48
+ const TSharedPtr<FJsonObject> Obj = CollisionResponseObject(Response);
49
+ if (!Obj.IsValid()) return FString();
50
+ FString Value;
51
+ Obj->TryGetStringField(Field, Value);
52
+ return Value;
53
+ }
54
+
55
+ /** Pull one channel's entry out of a `responses` array by its enumName,
56
+ * which is the identifier that does not move when a project renames a
57
+ * channel. Returns an unset pointer when the channel is not listed. */
58
+ TSharedPtr<FJsonObject> FindChannelEntry(const TSharedPtr<FJsonValue>& Response, const TCHAR* EnumName)
59
+ {
60
+ const TSharedPtr<FJsonObject> Obj = CollisionResponseObject(Response);
61
+ if (!Obj.IsValid()) return TSharedPtr<FJsonObject>();
62
+
63
+ const TArray<TSharedPtr<FJsonValue>>* Entries = nullptr;
64
+ if (!Obj->TryGetArrayField(TEXT("responses"), Entries) || !Entries)
65
+ {
66
+ return TSharedPtr<FJsonObject>();
67
+ }
68
+ for (const TSharedPtr<FJsonValue>& Entry : *Entries)
69
+ {
70
+ if (!Entry.IsValid() || Entry->Type != EJson::Object) continue;
71
+ const TSharedPtr<FJsonObject> EntryObj = Entry->AsObject();
72
+ FString Name;
73
+ if (EntryObj->TryGetStringField(TEXT("enumName"), Name) && Name == EnumName)
74
+ {
75
+ return EntryObj;
76
+ }
77
+ }
78
+ return TSharedPtr<FJsonObject>();
79
+ }
80
+ }
81
+
82
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
83
+ FMCPResolveCollisionProfileTest,
84
+ "UE.MCP.Project.ResolveCollisionProfile.ReportsPerChannelResponses",
85
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
86
+
87
+ bool FMCPResolveCollisionProfileTest::RunTest(const FString& Parameters)
88
+ {
89
+ FMCPHandlerRegistry Registry;
90
+ FCollisionQueryHandlers::RegisterHandlers(Registry);
91
+ if (!TestTrue(TEXT("resolve_collision_profile is registered"),
92
+ Registry.HasHandler(TEXT("resolve_collision_profile")))) return false;
93
+
94
+ {
95
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
96
+ Params->SetStringField(TEXT("profileName"), TEXT("Pawn"));
97
+
98
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("resolve_collision_profile"), Params);
99
+ TestTrue(FString::Printf(TEXT("the Pawn profile resolves (%s)"),
100
+ *CollisionResponseString(Response, TEXT("error"))),
101
+ CollisionResponseBool(Response, TEXT("success")));
102
+ TestEqual(TEXT("the profile answers under its own name"),
103
+ CollisionResponseString(Response, TEXT("profileName")), FString(TEXT("Pawn")));
104
+ TestFalse(TEXT("the profile names its collision mode"),
105
+ CollisionResponseString(Response, TEXT("collisionEnabled")).IsEmpty());
106
+
107
+ // The eight engine channels are always reported, which is the set a
108
+ // caller asking about Camera or Visibility means.
109
+ const TSharedPtr<FJsonObject> Camera = FindChannelEntry(Response, TEXT("ECC_Camera"));
110
+ if (TestTrue(TEXT("the Camera channel is listed"), Camera.IsValid()))
111
+ {
112
+ FString CameraResponse;
113
+ Camera->TryGetStringField(TEXT("response"), CameraResponse);
114
+ TestTrue(TEXT("the Camera response is one of the three collision responses"),
115
+ CameraResponse == TEXT("Block") || CameraResponse == TEXT("Overlap") || CameraResponse == TEXT("Ignore"));
116
+
117
+ double ChannelIndex = -1.0;
118
+ Camera->TryGetNumberField(TEXT("channelIndex"), ChannelIndex);
119
+ TestEqual(TEXT("the Camera channel reports its container index"), ChannelIndex, 4.0);
120
+ }
121
+ }
122
+
123
+ // One channel asked for, one channel back.
124
+ {
125
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
126
+ Params->SetStringField(TEXT("profileName"), TEXT("Pawn"));
127
+ Params->SetStringField(TEXT("channel"), TEXT("ECC_Camera"));
128
+
129
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("resolve_collision_profile"), Params);
130
+ TestTrue(TEXT("a single-channel query succeeds"), CollisionResponseBool(Response, TEXT("success")));
131
+
132
+ const TSharedPtr<FJsonObject> Obj = CollisionResponseObject(Response);
133
+ if (TestTrue(TEXT("the single-channel query answered"), Obj.IsValid()))
134
+ {
135
+ double Count = -1.0;
136
+ Obj->TryGetNumberField(TEXT("responseCount"), Count);
137
+ TestEqual(TEXT("exactly one channel comes back"), Count, 1.0);
138
+ TestTrue(TEXT("the one channel is lifted to the top level"), Obj->HasField(TEXT("channel")));
139
+ }
140
+ }
141
+
142
+ // A miss names what does exist rather than answering with nothing.
143
+ {
144
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
145
+ Params->SetStringField(TEXT("profileName"), TEXT("NoSuchProfileNameAnywhere"));
146
+
147
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("resolve_collision_profile"), Params);
148
+ TestFalse(TEXT("an unknown profile is not a success"),
149
+ CollisionResponseBool(Response, TEXT("success"), true));
150
+ TestEqual(TEXT("an unknown profile says why"),
151
+ CollisionResponseString(Response, TEXT("reason")), FString(TEXT("profile_not_found")));
152
+
153
+ const TSharedPtr<FJsonObject> Obj = CollisionResponseObject(Response);
154
+ if (Obj.IsValid())
155
+ {
156
+ const TArray<TSharedPtr<FJsonValue>>* Known = nullptr;
157
+ if (TestTrue(TEXT("an unknown profile lists the known ones"),
158
+ Obj->TryGetArrayField(TEXT("knownProfiles"), Known) && Known != nullptr))
159
+ {
160
+ TestTrue(TEXT("the project defines at least one profile"), Known->Num() > 0);
161
+ }
162
+ }
163
+ }
164
+
165
+ return true;
166
+ }
167
+
168
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
169
+ FMCPGetComponentCollisionTest,
170
+ "UE.MCP.Blueprint.GetComponentCollision.ReportsTheEffectiveResponse",
171
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
172
+
173
+ bool FMCPGetComponentCollisionTest::RunTest(const FString& Parameters)
174
+ {
175
+ FMCPHandlerRegistry Registry;
176
+ FCollisionQueryHandlers::RegisterHandlers(Registry);
177
+ if (!TestTrue(TEXT("get_component_collision is registered"),
178
+ Registry.HasHandler(TEXT("get_component_collision")))) return false;
179
+
180
+ const FString CapsuleName = ACharacter::CapsuleComponentName.ToString();
181
+
182
+ // The native-class route. This is the shape #925 was reported from:
183
+ // checking whether a C++ constructor's collision call reached the CDO.
184
+ {
185
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
186
+ Params->SetStringField(TEXT("assetPath"), TEXT("/Script/Engine.Character"));
187
+ Params->SetStringField(TEXT("componentName"), CapsuleName);
188
+
189
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("get_component_collision"), Params);
190
+ if (!TestTrue(FString::Printf(TEXT("the Character CDO capsule resolves (%s)"),
191
+ *CollisionResponseString(Response, TEXT("error"))),
192
+ CollisionResponseBool(Response, TEXT("success")))) return false;
193
+
194
+ TestEqual(TEXT("the answer says it came from a class rather than a Blueprint"),
195
+ CollisionResponseString(Response, TEXT("source")), FString(TEXT("class")));
196
+ TestEqual(TEXT("the capsule's profile is Pawn"),
197
+ CollisionResponseString(Response, TEXT("profileName")), FString(TEXT("Pawn")));
198
+ TestTrue(TEXT("the profile resolved to a real profile"),
199
+ CollisionResponseBool(Response, TEXT("profileResolved")));
200
+ TestFalse(TEXT("the answer names the collision mode"),
201
+ CollisionResponseString(Response, TEXT("collisionEnabled")).IsEmpty());
202
+
203
+ // The assertion the issue exists for. ACharacter's capsule never
204
+ // overrides Camera, so the only place its answer can come from is the
205
+ // Pawn profile, and the text dump the old actions produce does not
206
+ // contain it at all.
207
+ const TSharedPtr<FJsonObject> Camera = FindChannelEntry(Response, TEXT("ECC_Camera"));
208
+ if (TestTrue(TEXT("the Camera channel is reported"), Camera.IsValid()))
209
+ {
210
+ FString Effective;
211
+ Camera->TryGetStringField(TEXT("response"), Effective);
212
+ FString FromProfile;
213
+ Camera->TryGetStringField(TEXT("profileResponse"), FromProfile);
214
+
215
+ TestFalse(TEXT("the effective Camera response is reported"), Effective.IsEmpty());
216
+ TestEqual(TEXT("an inherited response equals the profile's own"), Effective, FromProfile);
217
+
218
+ bool bOverrides = true;
219
+ Camera->TryGetBoolField(TEXT("overridesProfile"), bOverrides);
220
+ TestFalse(TEXT("an inherited response is not reported as an override"), bOverrides);
221
+
222
+ // And it agrees with the engine's own evaluator, which is the
223
+ // definition of "effective".
224
+ if (UClass* CharacterClass = ACharacter::StaticClass())
225
+ {
226
+ if (ACharacter* CDO = Cast<ACharacter>(CharacterClass->GetDefaultObject()))
227
+ {
228
+ if (UCapsuleComponent* Capsule = CDO->GetCapsuleComponent())
229
+ {
230
+ const ECollisionResponse Engine = Capsule->GetCollisionResponseToChannel(ECC_Camera);
231
+ const FString EngineName =
232
+ Engine == ECR_Block ? TEXT("Block") :
233
+ Engine == ECR_Overlap ? TEXT("Overlap") :
234
+ Engine == ECR_Ignore ? TEXT("Ignore") : TEXT("Unknown");
235
+ TestEqual(TEXT("the reported response is what the engine evaluator returns"),
236
+ Effective, EngineName);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ // A channel name the project does not have is an error naming the ones it
244
+ // does, not an empty response.
245
+ {
246
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
247
+ Params->SetStringField(TEXT("assetPath"), TEXT("/Script/Engine.Character"));
248
+ Params->SetStringField(TEXT("componentName"), CapsuleName);
249
+ Params->SetStringField(TEXT("channel"), TEXT("NoSuchChannelName"));
250
+
251
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("get_component_collision"), Params);
252
+ TestFalse(TEXT("an unknown channel is not a success"),
253
+ CollisionResponseBool(Response, TEXT("success"), true));
254
+ TestTrue(TEXT("an unknown channel lists the known ones"),
255
+ CollisionResponseString(Response, TEXT("error")).Contains(TEXT("Camera")));
256
+ }
257
+
258
+ // A component with no BodyInstance says so rather than answering with an
259
+ // empty channel list.
260
+ {
261
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
262
+ Params->SetStringField(TEXT("assetPath"), TEXT("/Script/Engine.Character"));
263
+ Params->SetStringField(TEXT("componentName"), ACharacter::CharacterMovementComponentName.ToString());
264
+
265
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("get_component_collision"), Params);
266
+ TestFalse(TEXT("a non-primitive component is not a success"),
267
+ CollisionResponseBool(Response, TEXT("success"), true));
268
+ TestTrue(TEXT("a non-primitive component says it carries no collision"),
269
+ CollisionResponseString(Response, TEXT("error")).Contains(TEXT("carries no collision")));
270
+ }
271
+
272
+ // A component name that does not exist lists what does.
273
+ {
274
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
275
+ Params->SetStringField(TEXT("assetPath"), TEXT("/Script/Engine.Character"));
276
+ Params->SetStringField(TEXT("componentName"), TEXT("NoSuchComponent"));
277
+
278
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("get_component_collision"), Params);
279
+ TestFalse(TEXT("a missing component is not a success"),
280
+ CollisionResponseBool(Response, TEXT("success"), true));
281
+ TestTrue(TEXT("a missing component lists the available ones"),
282
+ CollisionResponseString(Response, TEXT("error")).Contains(CapsuleName));
283
+ }
284
+
285
+ return true;
286
+ }
287
+
288
+ #endif