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,526 @@
1
+ #if WITH_DEV_AUTOMATION_TESTS
2
+
3
+ // The engine contracts that BehaviorTree graph authoring rests on (#889, #947).
4
+ //
5
+ // add_bt_node, move_bt_node and remove_bt_node need a real asset path, so these
6
+ // tests do not call the handlers. They build the same BehaviorTree + graph pair
7
+ // in memory and drive the same engine calls in the same order, which is what
8
+ // actually needs pinning: every one of these behaviours is undocumented, and a
9
+ // silent change to any of them would leave the handlers compiling and producing
10
+ // trees that do not run.
11
+ //
12
+ // The contract that matters most is the ordering one. Child execution order is
13
+ // not stored anywhere: BTGraphHelpers::CreateChildren sorts each output pin's
14
+ // links by node X position on every compile, so a composite's child order IS
15
+ // the graph layout. Reordering a Selector's branches is therefore a position
16
+ // rewrite, and if that ever stops being true, reordering silently becomes a
17
+ // no-op rather than failing. FMCPBTAuthoringChildOrderTest is what would catch
18
+ // it.
19
+
20
+ #include "Handlers/GameplayHandlers.h"
21
+ #include "Misc/AutomationTest.h"
22
+
23
+ #include "Dom/JsonObject.h"
24
+ #include "Dom/JsonValue.h"
25
+ #include "UObject/Package.h"
26
+ #include "UObject/UObjectGlobals.h"
27
+
28
+ #include "EdGraph/EdGraph.h"
29
+ #include "EdGraph/EdGraphNode.h"
30
+ #include "EdGraph/EdGraphPin.h"
31
+ #include "EdGraph/EdGraphSchema.h"
32
+ #include "Kismet2/BlueprintEditorUtils.h"
33
+
34
+ #include "BehaviorTree/BehaviorTree.h"
35
+ #include "BehaviorTree/BehaviorTreeTypes.h"
36
+ #include "BehaviorTree/BlackboardData.h"
37
+ #include "BehaviorTree/Blackboard/BlackboardKeyType.h"
38
+ #include "BehaviorTree/BTCompositeNode.h"
39
+ #include "BehaviorTree/BTDecorator.h"
40
+ #include "BehaviorTree/BTNode.h"
41
+ #include "BehaviorTree/BTTaskNode.h"
42
+
43
+ #include "AIGraphTypes.h"
44
+ #include "BehaviorTreeGraph.h"
45
+ #include "BehaviorTreeGraphNode.h"
46
+ #include "BehaviorTreeGraphNode_Composite.h"
47
+ #include "BehaviorTreeGraphNode_Decorator.h"
48
+ #include "BehaviorTreeGraphNode_Root.h"
49
+ #include "BehaviorTreeGraphNode_Task.h"
50
+ #include "EdGraphSchema_BehaviorTree.h"
51
+
52
+ namespace
53
+ {
54
+ // Node classes are resolved by path rather than pulled in by header, so a
55
+ // build missing one of them skips the case instead of failing to link.
56
+ UClass* MCPBTAuthTestClass(const TCHAR* ClassPath)
57
+ {
58
+ return FindObject<UClass>(nullptr, ClassPath);
59
+ }
60
+
61
+ // A BehaviorTree with a freshly seeded editor graph, built exactly the way
62
+ // the authoring handlers seed one for a tree that has never been opened in
63
+ // the BehaviorTree editor.
64
+ struct FMCPBTAuthTestTree
65
+ {
66
+ UBehaviorTree* Tree = nullptr;
67
+ UBehaviorTreeGraph* Graph = nullptr;
68
+ UBehaviorTreeGraphNode_Root* Root = nullptr;
69
+
70
+ bool IsValid() const { return Tree && Graph && Root; }
71
+ };
72
+
73
+ FMCPBTAuthTestTree MCPBTAuthTestMakeTree()
74
+ {
75
+ FMCPBTAuthTestTree Out;
76
+ Out.Tree = NewObject<UBehaviorTree>(GetTransientPackage());
77
+ if (!Out.Tree) return Out;
78
+
79
+ Out.Tree->BTGraph = FBlueprintEditorUtils::CreateNewGraph(
80
+ Out.Tree,
81
+ FName(TEXT("Behavior Tree")),
82
+ UBehaviorTreeGraph::StaticClass(),
83
+ UEdGraphSchema_BehaviorTree::StaticClass());
84
+
85
+ Out.Graph = Cast<UBehaviorTreeGraph>(Out.Tree->BTGraph);
86
+ if (!Out.Graph) return Out;
87
+
88
+ if (const UEdGraphSchema* Schema = Out.Graph->GetSchema())
89
+ {
90
+ Schema->CreateDefaultNodesForGraph(*Out.Graph);
91
+ }
92
+ Out.Graph->OnCreated();
93
+
94
+ for (UEdGraphNode* Node : Out.Graph->Nodes)
95
+ {
96
+ if (UBehaviorTreeGraphNode_Root* Root = Cast<UBehaviorTreeGraphNode_Root>(Node))
97
+ {
98
+ Out.Root = Root;
99
+ break;
100
+ }
101
+ }
102
+ return Out;
103
+ }
104
+
105
+ // Place a graph node carrying RuntimeClass, the way add_bt_node does:
106
+ // ClassData first, then Finalize, which runs PostPlacedNewNode and spawns
107
+ // the UBTNode instance under the asset.
108
+ UBehaviorTreeGraphNode* MCPBTAuthTestPlace(
109
+ UBehaviorTreeGraph* Graph, UClass* GraphNodeClass, UClass* RuntimeClass, int32 PosX, int32 PosY)
110
+ {
111
+ if (!Graph || !GraphNodeClass || !RuntimeClass) return nullptr;
112
+
113
+ UBehaviorTreeGraphNode* Created = nullptr;
114
+ {
115
+ FGraphNodeCreator<UBehaviorTreeGraphNode> Creator(*Graph);
116
+ Created = Creator.CreateNode(/*bSelectNewNode*/ false, GraphNodeClass);
117
+ if (Created)
118
+ {
119
+ Created->ClassData = FGraphNodeClassData(RuntimeClass, FString());
120
+ }
121
+ Creator.Finalize();
122
+ }
123
+ if (Created)
124
+ {
125
+ Created->NodePosX = PosX;
126
+ Created->NodePosY = PosY;
127
+ }
128
+ return Created;
129
+ }
130
+
131
+ bool MCPBTAuthTestLink(UBehaviorTreeGraph* Graph, UBehaviorTreeGraphNode* Parent, UBehaviorTreeGraphNode* Child)
132
+ {
133
+ if (!Graph || !Parent || !Child) return false;
134
+ const UEdGraphSchema* Schema = Graph->GetSchema();
135
+ UEdGraphPin* Out = Parent->GetOutputPin(0);
136
+ UEdGraphPin* In = Child->GetInputPin(0);
137
+ if (!Schema || !Out || !In) return false;
138
+ return Schema->TryCreateConnection(Out, In);
139
+ }
140
+ }
141
+
142
+ // The whole authoring path in one pass: seed a graph, place a composite under
143
+ // the root, place two tasks under the composite, compile, then prove the
144
+ // compiled runtime tree is the one that was drawn.
145
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
146
+ FMCPBTAuthoringCompileTest,
147
+ "UE.MCP.Gameplay.BehaviorTree.AuthoringCompile",
148
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
149
+
150
+ bool FMCPBTAuthoringCompileTest::RunTest(const FString& Parameters)
151
+ {
152
+ UClass* SelectorClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTComposite_Selector"));
153
+ UClass* WaitClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTTask_Wait"));
154
+ if (!SelectorClass || !WaitClass)
155
+ {
156
+ AddInfo(TEXT("BTComposite_Selector or BTTask_Wait is not present in this build; skipping."));
157
+ return true;
158
+ }
159
+
160
+ FMCPBTAuthTestTree Fixture = MCPBTAuthTestMakeTree();
161
+ TestTrue(TEXT("a BehaviorTree graph can be seeded with its root node"), Fixture.IsValid());
162
+ if (!Fixture.IsValid()) return false;
163
+
164
+ // The seeded graph compiles to an empty tree, which is the state a caller
165
+ // starts from after gameplay(create_behavior_tree).
166
+ Fixture.Graph->UpdateAsset();
167
+ TestNull(TEXT("a graph with only a root compiles to no runtime root"), Fixture.Tree->RootNode.Get());
168
+
169
+ UBehaviorTreeGraphNode* Selector = MCPBTAuthTestPlace(
170
+ Fixture.Graph, UBehaviorTreeGraphNode_Composite::StaticClass(), SelectorClass, 0, 200);
171
+ TestNotNull(TEXT("the composite graph node was placed"), Selector);
172
+ if (!Selector) return false;
173
+
174
+ // PostPlacedNewNode is what turns ClassData into a runtime node instance
175
+ // owned by the asset. Without it every later step operates on nothing.
176
+ TestNotNull(TEXT("placing the node spawned its runtime instance"), Selector->NodeInstance.Get());
177
+ TestTrue(TEXT("the instance is owned by the BehaviorTree asset"),
178
+ Selector->NodeInstance && Selector->NodeInstance->GetOuter() == Fixture.Tree);
179
+
180
+ TestTrue(TEXT("the schema links the composite under the root"),
181
+ MCPBTAuthTestLink(Fixture.Graph, Fixture.Root, Selector));
182
+
183
+ UBehaviorTreeGraphNode* Left = MCPBTAuthTestPlace(
184
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, -300, 400);
185
+ UBehaviorTreeGraphNode* Right = MCPBTAuthTestPlace(
186
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, 300, 400);
187
+ TestTrue(TEXT("both task nodes were placed"), Left != nullptr && Right != nullptr);
188
+ if (!Left || !Right) return false;
189
+
190
+ TestTrue(TEXT("the first task links under the composite"), MCPBTAuthTestLink(Fixture.Graph, Selector, Left));
191
+ TestTrue(TEXT("the second task links under the composite"), MCPBTAuthTestLink(Fixture.Graph, Selector, Right));
192
+
193
+ // A task cannot sit directly under the root: the root's output pin is
194
+ // declared SingleComposite. The schema is what enforces this, which is why
195
+ // add_bt_node routes every link through it rather than writing pins.
196
+ UBehaviorTreeGraphNode* Stray = MCPBTAuthTestPlace(
197
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, 900, 400);
198
+ if (Stray)
199
+ {
200
+ TestFalse(TEXT("the schema refuses a task directly under the root"),
201
+ MCPBTAuthTestLink(Fixture.Graph, Fixture.Root, Stray));
202
+ Stray->DestroyNode();
203
+ }
204
+
205
+ Fixture.Graph->UpdateAsset();
206
+
207
+ UBTCompositeNode* RuntimeRoot = Fixture.Tree->RootNode.Get();
208
+ TestNotNull(TEXT("the graph compiled into a runtime root"), RuntimeRoot);
209
+ if (!RuntimeRoot) return false;
210
+ TestTrue(TEXT("the runtime root is the composite that was placed"),
211
+ static_cast<UObject*>(RuntimeRoot) == Selector->NodeInstance.Get());
212
+ TestEqual(TEXT("both tasks compiled in as children"), RuntimeRoot->Children.Num(), 2);
213
+
214
+ // A compiled node carries a real execution index. This is the step that
215
+ // reflection alone can never reach: a UBTNode spawned by script keeps
216
+ // MAX_uint16 here and the tree refuses to run.
217
+ TestTrue(TEXT("the runtime root has a real execution index"),
218
+ RuntimeRoot->GetExecutionIndex() != MAX_uint16);
219
+
220
+ // The addresses the read surface reports, derived from the compiled tree.
221
+ TMap<UBTNode*, FString> Addresses;
222
+ FGameplayHandlers::MapBTNodeAddresses(Fixture.Tree, Addresses);
223
+ const FString* RootAddress = Addresses.Find(RuntimeRoot);
224
+ TestTrue(TEXT("the compiled root has an address"), RootAddress != nullptr);
225
+ if (RootAddress) TestEqual(TEXT("the compiled root addresses as Root"), *RootAddress, FString(TEXT("Root")));
226
+
227
+ if (RuntimeRoot->Children.Num() == 2)
228
+ {
229
+ const FString* FirstChild = Addresses.Find(Cast<UBTNode>(RuntimeRoot->Children[0].ChildTask.Get()));
230
+ TestTrue(TEXT("the first child has an address"), FirstChild != nullptr);
231
+ if (FirstChild)
232
+ {
233
+ TestEqual(TEXT("the first child addresses as Root.Children[0]"),
234
+ *FirstChild, FString(TEXT("Root.Children[0]")));
235
+ }
236
+ }
237
+ return true;
238
+ }
239
+
240
+ // Child execution order is the graph's X layout, re-derived on every compile.
241
+ // This is the contract move_bt_node's reorder depends on, and the one that
242
+ // would break silently.
243
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
244
+ FMCPBTAuthoringChildOrderTest,
245
+ "UE.MCP.Gameplay.BehaviorTree.AuthoringChildOrder",
246
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
247
+
248
+ bool FMCPBTAuthoringChildOrderTest::RunTest(const FString& Parameters)
249
+ {
250
+ UClass* SequenceClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTComposite_Sequence"));
251
+ UClass* WaitClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTTask_Wait"));
252
+ if (!SequenceClass || !WaitClass)
253
+ {
254
+ AddInfo(TEXT("BTComposite_Sequence or BTTask_Wait is not present in this build; skipping."));
255
+ return true;
256
+ }
257
+
258
+ FMCPBTAuthTestTree Fixture = MCPBTAuthTestMakeTree();
259
+ if (!Fixture.IsValid())
260
+ {
261
+ AddError(TEXT("could not seed a BehaviorTree graph"));
262
+ return false;
263
+ }
264
+
265
+ UBehaviorTreeGraphNode* Sequence = MCPBTAuthTestPlace(
266
+ Fixture.Graph, UBehaviorTreeGraphNode_Composite::StaticClass(), SequenceClass, 0, 200);
267
+ UBehaviorTreeGraphNode* First = MCPBTAuthTestPlace(
268
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, -300, 400);
269
+ UBehaviorTreeGraphNode* Second = MCPBTAuthTestPlace(
270
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, 300, 400);
271
+ if (!Sequence || !First || !Second)
272
+ {
273
+ AddError(TEXT("could not place the nodes under test"));
274
+ return false;
275
+ }
276
+
277
+ MCPBTAuthTestLink(Fixture.Graph, Fixture.Root, Sequence);
278
+ MCPBTAuthTestLink(Fixture.Graph, Sequence, First);
279
+ MCPBTAuthTestLink(Fixture.Graph, Sequence, Second);
280
+ Fixture.Graph->UpdateAsset();
281
+
282
+ UBTCompositeNode* RuntimeRoot = Fixture.Tree->RootNode.Get();
283
+ if (!RuntimeRoot || RuntimeRoot->Children.Num() != 2)
284
+ {
285
+ AddError(TEXT("the sequence did not compile with two children"));
286
+ return false;
287
+ }
288
+
289
+ TestTrue(TEXT("the leftmost node compiles as child 0"),
290
+ static_cast<UObject*>(RuntimeRoot->Children[0].ChildTask.Get()) == First->NodeInstance.Get());
291
+ TestTrue(TEXT("the rightmost node compiles as child 1"),
292
+ static_cast<UObject*>(RuntimeRoot->Children[1].ChildTask.Get()) == Second->NodeInstance.Get());
293
+
294
+ // Swap the positions and nothing else. If ordering were held in the link
295
+ // array, or anywhere but the layout, this recompile would not move.
296
+ const int32 FirstX = First->NodePosX;
297
+ First->NodePosX = Second->NodePosX;
298
+ Second->NodePosX = FirstX;
299
+ Fixture.Graph->UpdateAsset();
300
+
301
+ RuntimeRoot = Fixture.Tree->RootNode.Get();
302
+ if (!RuntimeRoot || RuntimeRoot->Children.Num() != 2)
303
+ {
304
+ AddError(TEXT("the sequence lost its children on recompile"));
305
+ return false;
306
+ }
307
+
308
+ TestTrue(TEXT("swapping X positions swaps the execution order"),
309
+ static_cast<UObject*>(RuntimeRoot->Children[0].ChildTask.Get()) == Second->NodeInstance.Get());
310
+ TestTrue(TEXT("and the former first child now runs second"),
311
+ static_cast<UObject*>(RuntimeRoot->Children[1].ChildTask.Get()) == First->NodeInstance.Get());
312
+ return true;
313
+ }
314
+
315
+ // Reparenting: breaking a child's input link and relinking it under a different
316
+ // composite moves the whole branch, which is move_bt_node's reconnect path.
317
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
318
+ FMCPBTAuthoringReparentTest,
319
+ "UE.MCP.Gameplay.BehaviorTree.AuthoringReparent",
320
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
321
+
322
+ bool FMCPBTAuthoringReparentTest::RunTest(const FString& Parameters)
323
+ {
324
+ UClass* SelectorClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTComposite_Selector"));
325
+ UClass* SequenceClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTComposite_Sequence"));
326
+ UClass* WaitClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTTask_Wait"));
327
+ if (!SelectorClass || !SequenceClass || !WaitClass)
328
+ {
329
+ AddInfo(TEXT("the composite or task classes under test are absent from this build; skipping."));
330
+ return true;
331
+ }
332
+
333
+ FMCPBTAuthTestTree Fixture = MCPBTAuthTestMakeTree();
334
+ if (!Fixture.IsValid())
335
+ {
336
+ AddError(TEXT("could not seed a BehaviorTree graph"));
337
+ return false;
338
+ }
339
+
340
+ UBehaviorTreeGraphNode* Selector = MCPBTAuthTestPlace(
341
+ Fixture.Graph, UBehaviorTreeGraphNode_Composite::StaticClass(), SelectorClass, 0, 200);
342
+ UBehaviorTreeGraphNode* Sequence = MCPBTAuthTestPlace(
343
+ Fixture.Graph, UBehaviorTreeGraphNode_Composite::StaticClass(), SequenceClass, -300, 400);
344
+ UBehaviorTreeGraphNode* Task = MCPBTAuthTestPlace(
345
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, 300, 400);
346
+ if (!Selector || !Sequence || !Task)
347
+ {
348
+ AddError(TEXT("could not place the nodes under test"));
349
+ return false;
350
+ }
351
+
352
+ MCPBTAuthTestLink(Fixture.Graph, Fixture.Root, Selector);
353
+ MCPBTAuthTestLink(Fixture.Graph, Selector, Sequence);
354
+ MCPBTAuthTestLink(Fixture.Graph, Selector, Task);
355
+ Fixture.Graph->UpdateAsset();
356
+
357
+ UBTCompositeNode* RuntimeRoot = Fixture.Tree->RootNode.Get();
358
+ TestEqual(TEXT("the selector starts with two children"), RuntimeRoot ? RuntimeRoot->Children.Num() : 0, 2);
359
+
360
+ // Move the task under the sequence. The old link has to go first: the
361
+ // child's input pin holds a single connection, and the compiled child list
362
+ // is only correct once it has.
363
+ if (UEdGraphPin* TaskIn = Task->GetInputPin(0))
364
+ {
365
+ TaskIn->BreakAllPinLinks(true);
366
+ }
367
+ Task->NodePosX = -300;
368
+ Task->NodePosY = 600;
369
+ TestTrue(TEXT("the task relinks under the sequence"), MCPBTAuthTestLink(Fixture.Graph, Sequence, Task));
370
+ Fixture.Graph->UpdateAsset();
371
+
372
+ RuntimeRoot = Fixture.Tree->RootNode.Get();
373
+ if (!RuntimeRoot)
374
+ {
375
+ AddError(TEXT("the tree lost its root on recompile"));
376
+ return false;
377
+ }
378
+ TestEqual(TEXT("the selector is down to one child"), RuntimeRoot->Children.Num(), 1);
379
+ if (RuntimeRoot->Children.Num() != 1) return false;
380
+
381
+ UBTCompositeNode* RuntimeSequence = RuntimeRoot->Children[0].ChildComposite.Get();
382
+ TestTrue(TEXT("that child is the sequence"),
383
+ static_cast<UObject*>(RuntimeSequence) == Sequence->NodeInstance.Get());
384
+ if (!RuntimeSequence) return false;
385
+ TestEqual(TEXT("the sequence picked up the task"), RuntimeSequence->Children.Num(), 1);
386
+ if (RuntimeSequence->Children.Num() == 1)
387
+ {
388
+ TestTrue(TEXT("and it is the task that was moved"),
389
+ static_cast<UObject*>(RuntimeSequence->Children[0].ChildTask.Get()) == Task->NodeInstance.Get());
390
+ }
391
+
392
+ // Addresses follow the move, which is what lets a caller hand a guid to the
393
+ // authoring calls and the same node's address to the read calls.
394
+ TMap<UBTNode*, FString> Addresses;
395
+ FGameplayHandlers::MapBTNodeAddresses(Fixture.Tree, Addresses);
396
+ if (const FString* TaskAddress = Addresses.Find(Cast<UBTNode>(Task->NodeInstance.Get())))
397
+ {
398
+ TestEqual(TEXT("the moved task readdresses under the sequence"),
399
+ *TaskAddress, FString(TEXT("Root.Children[0].Children[0]")));
400
+ }
401
+ else
402
+ {
403
+ AddError(TEXT("the moved task has no address in the compiled tree"));
404
+ }
405
+ return true;
406
+ }
407
+
408
+ // Decorators are subnodes, not children. AddSubNode is what puts one on a node,
409
+ // and the compiler reads it off the owner's Decorators array into the parent
410
+ // composite's child entry. Linking a decorator as a child instead would compile
411
+ // to nothing at all.
412
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
413
+ FMCPBTAuthoringSubNodeTest,
414
+ "UE.MCP.Gameplay.BehaviorTree.AuthoringSubNode",
415
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
416
+
417
+ bool FMCPBTAuthoringSubNodeTest::RunTest(const FString& Parameters)
418
+ {
419
+ UClass* SelectorClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTComposite_Selector"));
420
+ UClass* WaitClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTTask_Wait"));
421
+ UClass* DecoratorClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BTDecorator_Blackboard"));
422
+ UClass* BoolKeyClass = MCPBTAuthTestClass(TEXT("/Script/AIModule.BlackboardKeyType_Bool"));
423
+ if (!SelectorClass || !WaitClass || !DecoratorClass)
424
+ {
425
+ AddInfo(TEXT("the composite, task or decorator classes under test are absent from this build; skipping."));
426
+ return true;
427
+ }
428
+
429
+ FMCPBTAuthTestTree Fixture = MCPBTAuthTestMakeTree();
430
+ if (!Fixture.IsValid())
431
+ {
432
+ AddError(TEXT("could not seed a BehaviorTree graph"));
433
+ return false;
434
+ }
435
+
436
+ // A blackboard with one key, so the decorator's FBlackboardKeySelector has
437
+ // something real to resolve against when InitializeFromAsset runs.
438
+ UBlackboardData* Blackboard = NewObject<UBlackboardData>(Fixture.Tree);
439
+ FBlackboardEntry Entry;
440
+ Entry.EntryName = TEXT("IsAlive");
441
+ if (BoolKeyClass)
442
+ {
443
+ Entry.KeyType = NewObject<UBlackboardKeyType>(Blackboard, BoolKeyClass);
444
+ }
445
+ Blackboard->Keys.Add(Entry);
446
+ Fixture.Tree->BlackboardAsset = Blackboard;
447
+
448
+ UBehaviorTreeGraphNode* Selector = MCPBTAuthTestPlace(
449
+ Fixture.Graph, UBehaviorTreeGraphNode_Composite::StaticClass(), SelectorClass, 0, 200);
450
+ UBehaviorTreeGraphNode* Task = MCPBTAuthTestPlace(
451
+ Fixture.Graph, UBehaviorTreeGraphNode_Task::StaticClass(), WaitClass, 0, 400);
452
+ if (!Selector || !Task)
453
+ {
454
+ AddError(TEXT("could not place the nodes under test"));
455
+ return false;
456
+ }
457
+
458
+ MCPBTAuthTestLink(Fixture.Graph, Fixture.Root, Selector);
459
+ MCPBTAuthTestLink(Fixture.Graph, Selector, Task);
460
+
461
+ // The subnode is constructed against the graph and handed to AddSubNode,
462
+ // which does the guid, the instance spawn and the parent bookkeeping.
463
+ UBehaviorTreeGraphNode* SubNode = NewObject<UBehaviorTreeGraphNode>(
464
+ Fixture.Graph, UBehaviorTreeGraphNode_Decorator::StaticClass());
465
+ SubNode->ClassData = FGraphNodeClassData(DecoratorClass, FString());
466
+ Task->AddSubNode(SubNode, Fixture.Graph);
467
+
468
+ TestEqual(TEXT("the decorator landed in the task's Decorators list"), Task->Decorators.Num(), 1);
469
+ TestEqual(TEXT("and not in its Services list"), Task->Services.Num(), 0);
470
+ TestNotNull(TEXT("AddSubNode spawned the decorator instance"), SubNode->NodeInstance.Get());
471
+
472
+ UBTNode* DecoratorInstance = Cast<UBTNode>(SubNode->NodeInstance);
473
+ if (DecoratorInstance)
474
+ {
475
+ FString WriteError;
476
+ TestTrue(TEXT("the decorator's blackboard key is writable before the compile"),
477
+ FGameplayHandlers::WriteBTNodeProperty(DecoratorInstance, TEXT("BlackboardKey.SelectedKeyName"),
478
+ MakeShared<FJsonValueString>(TEXT("IsAlive")), WriteError));
479
+ }
480
+
481
+ Fixture.Graph->UpdateAsset();
482
+
483
+ UBTCompositeNode* RuntimeRoot = Fixture.Tree->RootNode.Get();
484
+ if (!RuntimeRoot || RuntimeRoot->Children.Num() != 1)
485
+ {
486
+ AddError(TEXT("the selector did not compile with its one child"));
487
+ return false;
488
+ }
489
+
490
+ // The decorator is stored on the PARENT composite's child entry, not on the
491
+ // task. That asymmetry between the graph and the runtime tree is why the
492
+ // read surface addresses it as Root.Children[0].Decorators[0].
493
+ TestEqual(TEXT("the decorator compiled onto the child entry"),
494
+ RuntimeRoot->Children[0].Decorators.Num(), 1);
495
+
496
+ TMap<UBTNode*, FString> Addresses;
497
+ FGameplayHandlers::MapBTNodeAddresses(Fixture.Tree, Addresses);
498
+ if (DecoratorInstance)
499
+ {
500
+ if (const FString* Address = Addresses.Find(DecoratorInstance))
501
+ {
502
+ TestEqual(TEXT("the decorator addresses under its child entry"),
503
+ *Address, FString(TEXT("Root.Children[0].Decorators[0]")));
504
+ }
505
+ else
506
+ {
507
+ AddError(TEXT("the compiled decorator has no address"));
508
+ }
509
+
510
+ // UpdateAsset re-runs InitializeFromAsset on every node, which is what
511
+ // turns the key name into a resolved key id. A selector that never
512
+ // resolves leaves the decorator permanently false at runtime.
513
+ if (BoolKeyClass)
514
+ {
515
+ FString ReadError;
516
+ const TSharedPtr<FJsonValue> Read = FGameplayHandlers::ReadBTNodeProperty(
517
+ DecoratorInstance, TEXT("BlackboardKey.SelectedKeyName"), ReadError);
518
+ FString ReadName;
519
+ if (Read.IsValid()) Read->TryGetString(ReadName);
520
+ TestEqual(TEXT("the key name survives the compile"), ReadName, FString(TEXT("IsAlive")));
521
+ }
522
+ }
523
+ return true;
524
+ }
525
+
526
+ #endif // WITH_DEV_AUTOMATION_TESTS