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
@@ -149,6 +149,23 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ScanAnimationTracks(const TSharedPtr<
149
149
  const FString Directory = OptionalString(Params, TEXT("directory"), TEXT(""));
150
150
  const FString SkeletonFilter = OptionalString(Params, TEXT("skeletonPath"));
151
151
 
152
+ // #890: the filter used to be compared as a string against
153
+ // USkeleton::GetPathName(), which is always the object path form, so a
154
+ // package path matched zero sequences and the scan still answered success.
155
+ // A caller could not tell that from a project that genuinely has no
156
+ // sequences on that skeleton. Resolve the filter to the skeleton itself and
157
+ // compare objects, and refuse a filter that names no skeleton at all rather
158
+ // than reporting it as a legitimate empty result.
159
+ USkeleton* FilterSkeleton = nullptr;
160
+ if (!SkeletonFilter.IsEmpty())
161
+ {
162
+ FilterSkeleton = LoadAssetByPath<USkeleton>(SkeletonFilter);
163
+ if (!FilterSkeleton)
164
+ {
165
+ return MCPAssetLoadError(SkeletonFilter, TEXT("Skeleton"));
166
+ }
167
+ }
168
+
152
169
  TArray<FString> AssetPaths;
153
170
  const TArray<TSharedPtr<FJsonValue>>* PathsArray = nullptr;
154
171
  if (Params->TryGetArrayField(TEXT("assetPaths"), PathsArray))
@@ -185,11 +202,11 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ScanAnimationTracks(const TSharedPtr<
185
202
  int32 ProblemCount = 0;
186
203
  int32 InspectedCount = 0;
187
204
  int32 FailureCount = 0;
205
+ int32 FilteredOutCount = 0;
188
206
 
189
207
  for (const FString& AssetPath : AssetPaths)
190
208
  {
191
- UObject* LoadedAsset = UEditorAssetLibrary::LoadAsset(AssetPath);
192
- UAnimSequence* AnimSeq = Cast<UAnimSequence>(LoadedAsset);
209
+ UAnimSequence* AnimSeq = LoadAssetByPath<UAnimSequence>(AssetPath);
193
210
  if (!AnimSeq)
194
211
  {
195
212
  ++FailureCount;
@@ -205,8 +222,9 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ScanAnimationTracks(const TSharedPtr<
205
222
 
206
223
  USkeleton* Skeleton = AnimSeq->GetSkeleton();
207
224
  const FString SequenceSkeletonPath = Skeleton ? Skeleton->GetPathName() : FString();
208
- if (!SkeletonFilter.IsEmpty() && SequenceSkeletonPath != SkeletonFilter)
225
+ if (FilterSkeleton && Skeleton != FilterSkeleton)
209
226
  {
227
+ ++FilteredOutCount;
210
228
  continue;
211
229
  }
212
230
 
@@ -257,10 +275,25 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ScanAnimationTracks(const TSharedPtr<
257
275
  auto Result = MCPSuccess();
258
276
  Result->SetStringField(TEXT("directory"), Directory);
259
277
  Result->SetNumberField(TEXT("targetTrackCount"), TargetTrackCount);
278
+ Result->SetNumberField(TEXT("candidateCount"), AssetPaths.Num());
260
279
  Result->SetNumberField(TEXT("inspectedCount"), InspectedCount);
261
280
  Result->SetNumberField(TEXT("problemCount"), ProblemCount);
262
281
  Result->SetNumberField(TEXT("failureCount"), FailureCount);
263
282
  Result->SetArrayField(TEXT("sequences"), Sequences);
283
+ if (FilterSkeleton)
284
+ {
285
+ // The resolved form, so a caller who passed a package path can see
286
+ // which skeleton the filter actually became (#890).
287
+ Result->SetStringField(TEXT("skeletonPath"), FilterSkeleton->GetPathName());
288
+ Result->SetNumberField(TEXT("filteredOutCount"), FilteredOutCount);
289
+ if (InspectedCount == 0)
290
+ {
291
+ Result->SetStringField(TEXT("note"), FString::Printf(
292
+ TEXT("The skeletonPath filter resolved to '%s' and matched none of the %d AnimSequence(s) scanned. ")
293
+ TEXT("This is a real empty result, not a path-shape miss."),
294
+ *FilterSkeleton->GetPathName(), AssetPaths.Num()));
295
+ }
296
+ }
264
297
  return MCPResult(Result);
265
298
  }
266
299
 
@@ -398,6 +431,10 @@ TSharedPtr<FJsonValue> FAnimationHandlers::SetBoneKeyframes(const TSharedPtr<FJs
398
431
  // Ensure bone track exists - add it if not present
399
432
  const FName BoneFName(*BoneName);
400
433
  const IAnimationDataModel* DataModel = AnimSeq->GetDataModel();
434
+ if (!DataModel)
435
+ {
436
+ return MCPError(TEXT("Animation sequence has no data model, so its bone tracks cannot be read or written"));
437
+ }
401
438
  if (!DataModel->IsValidBoneTrackName(BoneFName))
402
439
  {
403
440
  Controller.AddBoneCurve(BoneFName);
@@ -530,6 +567,10 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BakeKeyframesBatch(const TSharedPtr<F
530
567
  // Critical: create the bone track before writing keys, otherwise
531
568
  // SetBoneTrackKeys returns false and the asset stays a T-pose.
532
569
  const IAnimationDataModel* DataModel = AnimSeq->GetDataModel();
570
+ if (!DataModel)
571
+ {
572
+ return MCPError(TEXT("Animation sequence has no data model, so its bone tracks cannot be read or written"));
573
+ }
533
574
  if (!DataModel->IsValidBoneTrackName(BoneFName))
534
575
  {
535
576
  Controller.AddBoneCurve(BoneFName);
@@ -206,14 +206,36 @@ namespace
206
206
  return MCPError(TEXT("world must be 'auto' (default), 'pie', 'game', or 'editor'"));
207
207
  }
208
208
 
209
+ // #983: actorPath wins over the label token, and a label naming more
210
+ // than one actor in the world that answered is refused rather than
211
+ // read off whichever the iterator reached first.
212
+ const FString ActorPath = OptionalString(Params, TEXT("actorPath"));
209
213
  TArray<TPair<FString, UWorld*>> Candidates = BuildWorldCandidates(RequestedScope);
210
214
  for (const TPair<FString, UWorld*>& Candidate : Candidates)
211
215
  {
212
- AActor* Actor = FindActorByLabelNameOrPath(Candidate.Value, ActorToken);
213
- if (Actor)
216
+ if (!ActorPath.IsEmpty())
217
+ {
218
+ if (AActor* ByPath = MCPFindActorByPath(Candidate.Value, ActorPath))
219
+ {
220
+ OutWorld = Candidate.Value;
221
+ OutActor = ByPath;
222
+ OutResolvedScope = Candidate.Key;
223
+ return nullptr;
224
+ }
225
+ continue;
226
+ }
227
+ TArray<AActor*> Matches;
228
+ MCPCollectActorsByToken(Candidate.Value, ActorToken, EMCPActorMatch::LabelNameOrPath, Matches);
229
+ if (Matches.Num() > 1)
230
+ {
231
+ return MCPAmbiguousActorError(
232
+ ActorToken, TEXT("actorLabel"), TEXT("actorPath"),
233
+ MCPDescribeActorMatchTier(ActorToken, Matches[0]), Matches);
234
+ }
235
+ if (Matches.Num() == 1)
214
236
  {
215
237
  OutWorld = Candidate.Value;
216
- OutActor = Actor;
238
+ OutActor = Matches[0];
217
239
  OutResolvedScope = Candidate.Key;
218
240
  return nullptr;
219
241
  }
@@ -230,7 +252,7 @@ namespace
230
252
  TSharedPtr<FJsonValue> FAnimationHandlers::GetBoneTransform(const TSharedPtr<FJsonObject>& Params)
231
253
  {
232
254
  FString ActorLabel;
233
- if (auto Err = RequireString(Params, TEXT("actorLabel"), ActorLabel)) return Err;
255
+ if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
234
256
  FString BoneName;
235
257
  if (auto Err = RequireString(Params, TEXT("boneName"), BoneName)) return Err;
236
258
  const FString ComponentName = OptionalString(Params, TEXT("componentName"));
@@ -240,6 +262,9 @@ TSharedPtr<FJsonValue> FAnimationHandlers::GetBoneTransform(const TSharedPtr<FJs
240
262
  AActor* Actor = nullptr;
241
263
  FString ResolvedWorldScope;
242
264
  if (auto Err = ResolveSkeletalActorForQuery(Params, ActorLabel, TEXT("auto"), World, Actor, ResolvedWorldScope)) return Err;
265
+ // A caller who passed only actorPath left ActorLabel holding the path, and
266
+ // every message below reads better naming the actor that answered (#983).
267
+ ActorLabel = Actor->GetActorLabel();
243
268
  USkeletalMeshComponent* SK = ResolveSkeletalMeshComp(Actor, ComponentName);
244
269
  if (!SK) return MakeSkeletalComponentNotFoundError(Actor, ActorLabel, ComponentName);
245
270
 
@@ -292,13 +317,16 @@ TSharedPtr<FJsonValue> FAnimationHandlers::GetBoneTransform(const TSharedPtr<FJs
292
317
  TSharedPtr<FJsonValue> FAnimationHandlers::ListBones(const TSharedPtr<FJsonObject>& Params)
293
318
  {
294
319
  FString ActorLabel;
295
- if (auto Err = RequireString(Params, TEXT("actorLabel"), ActorLabel)) return Err;
320
+ if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
296
321
  const FString ComponentName = OptionalString(Params, TEXT("componentName"));
297
322
 
298
323
  UWorld* World = nullptr;
299
324
  AActor* Actor = nullptr;
300
325
  FString ResolvedWorldScope;
301
326
  if (auto Err = ResolveSkeletalActorForQuery(Params, ActorLabel, TEXT("auto"), World, Actor, ResolvedWorldScope)) return Err;
327
+ // A caller who passed only actorPath left ActorLabel holding the path, and
328
+ // every message below reads better naming the actor that answered (#983).
329
+ ActorLabel = Actor->GetActorLabel();
302
330
  USkeletalMeshComponent* SK = ResolveSkeletalMeshComp(Actor, ComponentName);
303
331
  if (!SK) return MakeSkeletalComponentNotFoundError(Actor, ActorLabel, ComponentName);
304
332
  if (!SK->GetSkeletalMeshAsset()) return MCPError(FString::Printf(TEXT("SkeletalMeshComponent '%s' on actor '%s' has no SkeletalMesh asset"), *SK->GetName(), *ActorLabel));
@@ -335,10 +363,12 @@ TSharedPtr<FJsonValue> FAnimationHandlers::RebindLeaderPose(const TSharedPtr<FJs
335
363
  {
336
364
  REQUIRE_EDITOR_WORLD(World);
337
365
  FString ActorLabel;
338
- if (auto Err = RequireString(Params, TEXT("actorLabel"), ActorLabel)) return Err;
366
+ if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
339
367
 
340
- AActor* Actor = FindActorByLabel(World, ActorLabel);
341
- if (!Actor) return MCPError(FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
368
+ TSharedPtr<FJsonValue> ActorErr;
369
+ AActor* Actor = MCPResolveActor(World, Params, ActorErr);
370
+ if (!Actor) return ActorErr;
371
+ ActorLabel = Actor->GetActorLabel();
342
372
 
343
373
  TArray<USkeletalMeshComponent*> Comps;
344
374
  Actor->GetComponents<USkeletalMeshComponent>(Comps);
@@ -374,6 +404,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::RebindLeaderPose(const TSharedPtr<FJs
374
404
  auto Result = MCPSuccess();
375
405
  MCPSetUpdated(Result);
376
406
  Result->SetStringField(TEXT("actorLabel"), ActorLabel);
407
+ Result->SetStringField(TEXT("actorPath"), Actor->GetPathName());
377
408
  Result->SetStringField(TEXT("body"), Body->GetName());
378
409
  Result->SetNumberField(TEXT("rebound"), Rebound);
379
410
  Result->SetArrayField(TEXT("components"), Bound);
@@ -385,12 +416,14 @@ TSharedPtr<FJsonValue> FAnimationHandlers::PreviewAnimation(const TSharedPtr<FJs
385
416
  {
386
417
  REQUIRE_EDITOR_WORLD(World);
387
418
  FString ActorLabel;
388
- if (auto Err = RequireString(Params, TEXT("actorLabel"), ActorLabel)) return Err;
419
+ if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
389
420
  bool bEnabled = true;
390
421
  Params->TryGetBoolField(TEXT("enabled"), bEnabled);
391
422
 
392
- AActor* Actor = FindActorByLabel(World, ActorLabel);
393
- if (!Actor) return MCPError(FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
423
+ TSharedPtr<FJsonValue> ActorErr;
424
+ AActor* Actor = MCPResolveActor(World, Params, ActorErr);
425
+ if (!Actor) return ActorErr;
426
+ ActorLabel = Actor->GetActorLabel();
394
427
 
395
428
  TArray<USkeletalMeshComponent*> Comps;
396
429
  Actor->GetComponents<USkeletalMeshComponent>(Comps);
@@ -413,12 +446,185 @@ TSharedPtr<FJsonValue> FAnimationHandlers::PreviewAnimation(const TSharedPtr<FJs
413
446
  auto Result = MCPSuccess();
414
447
  MCPSetUpdated(Result);
415
448
  Result->SetStringField(TEXT("actorLabel"), ActorLabel);
449
+ Result->SetStringField(TEXT("actorPath"), Actor->GetPathName());
416
450
  Result->SetBoolField(TEXT("enabled"), bEnabled);
417
451
  Result->SetNumberField(TEXT("componentsUpdated"), Updated);
418
452
 
419
453
  TSharedPtr<FJsonObject> Payload = MakeShared<FJsonObject>();
420
454
  Payload->SetStringField(TEXT("actorLabel"), ActorLabel);
455
+ Payload->SetStringField(TEXT("actorPath"), Actor->GetPathName());
421
456
  Payload->SetBoolField(TEXT("enabled"), !bEnabled);
422
457
  MCPSetRollback(Result, TEXT("preview_animation"), Payload);
423
458
  return MCPResult(Result);
424
459
  }
460
+
461
+
462
+ // #922/#926: the EVALUATED pose off a live SkeletalMeshComponent, in the editor
463
+ // world or in PIE.
464
+ //
465
+ // get_bone_transforms reads a skeleton's REFERENCE pose, which is the read that
466
+ // looks correct while the running instance is wrong: in #922 a character lay
467
+ // flat on the floor and every transform the bridge could show read clean,
468
+ // because none of them were the pose the component was actually holding.
469
+ // get_bone_transform answers for one bone at a time. This answers for a set, off
470
+ // the component's own evaluated arrays, and reports what is driving the
471
+ // evaluation next to the numbers, so a flat character and a stopped anim
472
+ // instance are distinguishable from one call.
473
+ TSharedPtr<FJsonValue> FAnimationHandlers::GetLiveBoneTransforms(const TSharedPtr<FJsonObject>& Params)
474
+ {
475
+ // How many bones one response carries. A humanoid skeleton is a few hundred,
476
+ // so the whole thing fits; a crowd-scale or facial rig can be far more.
477
+ constexpr int32 LiveBoneTransformCap = 1000;
478
+
479
+ FString ActorLabel;
480
+ if (auto Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), ActorLabel)) return Err;
481
+ const FString ComponentName = OptionalString(Params, TEXT("componentName"));
482
+ const FString Space = OptionalString(Params, TEXT("space"), TEXT("world")).ToLower();
483
+ if (!(Space == TEXT("world") || Space == TEXT("component") || Space == TEXT("local")))
484
+ {
485
+ return MCPError(TEXT("space must be 'world' (default), 'component', or 'local'"));
486
+ }
487
+
488
+ UWorld* World = nullptr;
489
+ AActor* Actor = nullptr;
490
+ FString ResolvedWorldScope;
491
+ if (auto Err = ResolveSkeletalActorForQuery(Params, ActorLabel, TEXT("auto"), World, Actor, ResolvedWorldScope)) return Err;
492
+ // A caller who passed only actorPath left ActorLabel holding the path, and
493
+ // every message below reads better naming the actor that answered (#983).
494
+ ActorLabel = Actor->GetActorLabel();
495
+ USkeletalMeshComponent* SK = ResolveSkeletalMeshComp(Actor, ComponentName);
496
+ if (!SK) return MakeSkeletalComponentNotFoundError(Actor, ActorLabel, ComponentName);
497
+ USkeletalMesh* Mesh = SK->GetSkeletalMeshAsset();
498
+ if (!Mesh)
499
+ {
500
+ return MCPError(FString::Printf(
501
+ TEXT("SkeletalMeshComponent '%s' on actor '%s' has no SkeletalMesh asset"), *SK->GetName(), *ActorLabel));
502
+ }
503
+
504
+ const FReferenceSkeleton& RefSkeleton = Mesh->GetRefSkeleton();
505
+
506
+ // boneNames selects a subset; omitting it returns every bone, which is what a
507
+ // caller diagnosing a pose wants and is a few hundred entries on a character.
508
+ TArray<FName> RequestedBones;
509
+ const TArray<TSharedPtr<FJsonValue>>* BonesArray = nullptr;
510
+ if (Params->TryGetArrayField(TEXT("boneNames"), BonesArray) && BonesArray)
511
+ {
512
+ for (const TSharedPtr<FJsonValue>& Entry : *BonesArray)
513
+ {
514
+ if (!Entry.IsValid()) continue;
515
+ const FString Name = Entry->AsString();
516
+ if (!Name.IsEmpty()) RequestedBones.Add(FName(*Name));
517
+ }
518
+ }
519
+ if (RequestedBones.Num() == 0)
520
+ {
521
+ for (int32 BoneIndex = 0; BoneIndex < RefSkeleton.GetNum(); ++BoneIndex)
522
+ {
523
+ RequestedBones.Add(RefSkeleton.GetBoneName(BoneIndex));
524
+ }
525
+ }
526
+ if (RequestedBones.Num() > LiveBoneTransformCap)
527
+ {
528
+ return MCPError(FString::Printf(
529
+ TEXT("%d bones requested, over the %d limit. Pass 'boneNames' with the bones you need."),
530
+ RequestedBones.Num(), LiveBoneTransformCap));
531
+ }
532
+
533
+ const TArray<FTransform>& ComponentSpace = SK->GetComponentSpaceTransforms();
534
+ const TArray<FTransform> BoneSpace = SK->GetBoneSpaceTransforms();
535
+
536
+ TArray<TSharedPtr<FJsonValue>> Bones;
537
+ TArray<TSharedPtr<FJsonValue>> Missing;
538
+ Bones.Reserve(RequestedBones.Num());
539
+ for (const FName& BoneName : RequestedBones)
540
+ {
541
+ const int32 BoneIndex = SK->GetBoneIndex(BoneName);
542
+ if (BoneIndex == INDEX_NONE)
543
+ {
544
+ Missing.Add(MakeShared<FJsonValueString>(BoneName.ToString()));
545
+ continue;
546
+ }
547
+
548
+ FTransform Transform;
549
+ if (Space == TEXT("world"))
550
+ {
551
+ Transform = SK->GetBoneTransform(BoneIndex);
552
+ }
553
+ else if (Space == TEXT("component"))
554
+ {
555
+ if (!ComponentSpace.IsValidIndex(BoneIndex))
556
+ {
557
+ Missing.Add(MakeShared<FJsonValueString>(BoneName.ToString()));
558
+ continue;
559
+ }
560
+ Transform = ComponentSpace[BoneIndex];
561
+ }
562
+ else
563
+ {
564
+ if (!BoneSpace.IsValidIndex(BoneIndex))
565
+ {
566
+ Missing.Add(MakeShared<FJsonValueString>(BoneName.ToString()));
567
+ continue;
568
+ }
569
+ Transform = BoneSpace[BoneIndex];
570
+ }
571
+
572
+ TSharedPtr<FJsonObject> BoneObj = MakeShared<FJsonObject>();
573
+ BoneObj->SetStringField(TEXT("name"), BoneName.ToString());
574
+ BoneObj->SetNumberField(TEXT("index"), BoneIndex);
575
+ const int32 ParentIndex = RefSkeleton.GetParentIndex(BoneIndex);
576
+ BoneObj->SetNumberField(TEXT("parentIndex"), ParentIndex);
577
+ if (ParentIndex != INDEX_NONE)
578
+ {
579
+ BoneObj->SetStringField(TEXT("parentName"), RefSkeleton.GetBoneName(ParentIndex).ToString());
580
+ }
581
+ BoneObj->SetObjectField(TEXT("location"), MCPVec3ToJsonObject(Transform.GetLocation()));
582
+ BoneObj->SetObjectField(TEXT("rotation"), MCPRotatorToJsonObject(Transform.GetRotation().Rotator()));
583
+ BoneObj->SetObjectField(TEXT("scale"), MCPVec3ToJsonObject(Transform.GetScale3D()));
584
+ Bones.Add(MakeShared<FJsonValueObject>(BoneObj));
585
+ }
586
+
587
+ auto Result = MCPSuccess();
588
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
589
+ Result->SetStringField(TEXT("actorName"), Actor->GetName());
590
+ Result->SetStringField(TEXT("actorPath"), Actor->GetPathName());
591
+ Result->SetStringField(TEXT("world"), ResolvedWorldScope);
592
+ Result->SetStringField(TEXT("worldName"), World ? World->GetName() : TEXT(""));
593
+ AddSkeletalComponentMetadata(Result, SK);
594
+ Result->SetStringField(TEXT("space"), Space);
595
+ Result->SetNumberField(TEXT("boneCount"), Bones.Num());
596
+ Result->SetArrayField(TEXT("bones"), Bones);
597
+ if (Missing.Num() > 0)
598
+ {
599
+ Result->SetArrayField(TEXT("unresolvedBones"), Missing);
600
+ }
601
+
602
+ // The component's own placement, so a component-space read lifts to world
603
+ // without a second call, and a component transform that reads clean while
604
+ // the pose does not is visible side by side (#922).
605
+ const FTransform ComponentToWorld = SK->GetComponentTransform();
606
+ TSharedPtr<FJsonObject> ComponentTransform = MakeShared<FJsonObject>();
607
+ ComponentTransform->SetObjectField(TEXT("location"), MCPVec3ToJsonObject(ComponentToWorld.GetLocation()));
608
+ ComponentTransform->SetObjectField(TEXT("rotation"), MCPRotatorToJsonObject(ComponentToWorld.GetRotation().Rotator()));
609
+ ComponentTransform->SetObjectField(TEXT("scale"), MCPVec3ToJsonObject(ComponentToWorld.GetScale3D()));
610
+ Result->SetObjectField(TEXT("componentTransform"), ComponentTransform);
611
+
612
+ // What is driving the pose. A stopped or absent anim instance is the usual
613
+ // reason an evaluated read matches the reference pose exactly.
614
+ TSharedPtr<FJsonObject> Evaluation = MakeShared<FJsonObject>();
615
+ const EAnimationMode::Type AnimationMode = SK->GetAnimationMode();
616
+ Evaluation->SetStringField(TEXT("animationMode"),
617
+ AnimationMode == EAnimationMode::AnimationBlueprint ? TEXT("AnimationBlueprint")
618
+ : AnimationMode == EAnimationMode::AnimationSingleNode ? TEXT("AnimationSingleNode")
619
+ : TEXT("AnimationCustomMode"));
620
+ if (UAnimInstance* AnimInstance = SK->GetAnimInstance())
621
+ {
622
+ Evaluation->SetStringField(TEXT("animInstanceClass"), AnimInstance->GetClass()->GetPathName());
623
+ }
624
+ Evaluation->SetNumberField(TEXT("componentSpaceTransformCount"), ComponentSpace.Num());
625
+ Evaluation->SetNumberField(TEXT("refSkeletonBoneCount"), RefSkeleton.GetNum());
626
+ Evaluation->SetBoolField(TEXT("componentVisible"), SK->IsVisible());
627
+ Result->SetObjectField(TEXT("evaluation"), Evaluation);
628
+
629
+ return MCPResult(Result);
630
+ }