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,265 @@
1
+ // Native coverage for snap_instances_to_surface. All mutation coverage uses a
2
+ // transient test world and transient actors, never project packages or maps.
3
+
4
+ #if WITH_DEV_AUTOMATION_TESTS
5
+
6
+ #include "HandlerRegistry.h"
7
+ #include "Handlers/LevelHandlers.h"
8
+ #include "Handlers/LevelHandlers_InstanceProjection_Internal.h"
9
+ #include "Components/BoxComponent.h"
10
+ #include "Components/InstancedStaticMeshComponent.h"
11
+ #include "Engine/Engine.h"
12
+ #include "Engine/EngineBaseTypes.h"
13
+ #include "Engine/World.h"
14
+ #include "EngineUtils.h"
15
+ #include "GameFramework/Actor.h"
16
+ #include "Misc/AutomationTest.h"
17
+
18
+ namespace
19
+ {
20
+ class FScopedProjectionTestWorld
21
+ {
22
+ public:
23
+ FScopedProjectionTestWorld()
24
+ {
25
+ check(GEngine);
26
+ const FName WorldName = MakeUniqueObjectName(
27
+ nullptr,
28
+ UWorld::StaticClass(),
29
+ TEXT("UEMCPInstanceProjectionTestWorld"),
30
+ EUniqueObjectNameOptions::GloballyUnique);
31
+ FWorldContext& Context = GEngine->CreateNewWorldContext(EWorldType::Game);
32
+ World = UWorld::CreateWorld(EWorldType::Game, false, WorldName, GetTransientPackage());
33
+ check(World);
34
+ World->AddToRoot();
35
+ Context.SetCurrentWorld(World);
36
+ World->InitializeActorsForPlay(FURL());
37
+ check(World->GetPhysicsScene());
38
+ }
39
+
40
+ ~FScopedProjectionTestWorld()
41
+ {
42
+ if (!World) return;
43
+ if (World->AreActorsInitialized())
44
+ {
45
+ for (AActor* Actor : FActorRange(World))
46
+ {
47
+ if (Actor) Actor->RouteEndPlay(EEndPlayReason::LevelTransition);
48
+ }
49
+ }
50
+ GEngine->ShutdownWorldNetDriver(World);
51
+ World->DestroyWorld(true);
52
+ World->SetPhysicsScene(nullptr);
53
+ GEngine->DestroyWorldContext(World);
54
+ World->RemoveFromRoot();
55
+ World = nullptr;
56
+ }
57
+
58
+ UWorld* Get() const { return World; }
59
+
60
+ private:
61
+ UWorld* World = nullptr;
62
+ };
63
+
64
+ AActor* SpawnInstanceProjectionTestActor(UWorld* World, const FString& Label)
65
+ {
66
+ FActorSpawnParameters SpawnParameters;
67
+ SpawnParameters.Name = MakeUniqueObjectName(
68
+ World->PersistentLevel,
69
+ AActor::StaticClass(),
70
+ FName(*Label));
71
+ SpawnParameters.ObjectFlags |= RF_Transient;
72
+ AActor* Actor = World->SpawnActor<AActor>(
73
+ AActor::StaticClass(),
74
+ FTransform::Identity,
75
+ SpawnParameters);
76
+ if (Actor) Actor->SetActorLabel(Label, false);
77
+ return Actor;
78
+ }
79
+
80
+ UInstancedStaticMeshComponent* AddInstanceComponent(
81
+ AActor* Actor,
82
+ const TArray<FTransform>& WorldTransforms)
83
+ {
84
+ UInstancedStaticMeshComponent* Instances = NewObject<UInstancedStaticMeshComponent>(
85
+ Actor,
86
+ TEXT("ProjectedInstances"),
87
+ RF_Transient | RF_Transactional);
88
+ Actor->SetRootComponent(Instances);
89
+ Actor->AddInstanceComponent(Instances);
90
+ Instances->SetMobility(EComponentMobility::Movable);
91
+ Instances->SetCollisionEnabled(ECollisionEnabled::NoCollision);
92
+ Instances->RegisterComponent();
93
+ for (const FTransform& Transform : WorldTransforms)
94
+ {
95
+ Instances->AddInstance(Transform, true);
96
+ }
97
+ return Instances;
98
+ }
99
+
100
+ UBoxComponent* AddSurfaceBox(AActor* Actor, const FVector& Extent)
101
+ {
102
+ UBoxComponent* Box = NewObject<UBoxComponent>(
103
+ Actor,
104
+ TEXT("ProjectionSurface"),
105
+ RF_Transient);
106
+ Actor->SetRootComponent(Box);
107
+ Actor->AddInstanceComponent(Box);
108
+ Box->SetBoxExtent(Extent, false);
109
+ Box->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
110
+ Box->SetCollisionObjectType(ECC_WorldStatic);
111
+ Box->SetCollisionResponseToAllChannels(ECR_Ignore);
112
+ Box->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
113
+ Box->RegisterComponent();
114
+ return Box;
115
+ }
116
+
117
+ TSharedPtr<FJsonObject> MakeProjectionRequest(
118
+ const FString& ActorLabel,
119
+ const FString& SurfaceLabel)
120
+ {
121
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
122
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
123
+ Request->SetStringField(TEXT("componentName"), TEXT("ProjectedInstances"));
124
+ Request->SetArrayField(
125
+ TEXT("surfaceActorLabels"),
126
+ { MakeShared<FJsonValueString>(SurfaceLabel) });
127
+ Request->SetNumberField(TEXT("surfaceOffset"), 10.0);
128
+ return Request;
129
+ }
130
+
131
+ bool ReadWorldTransform(
132
+ UInstancedStaticMeshComponent* Instances,
133
+ int32 Index,
134
+ FTransform& OutTransform)
135
+ {
136
+ return Instances && Instances->GetInstanceTransform(Index, OutTransform, true);
137
+ }
138
+ }
139
+
140
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
141
+ FLevelInstanceProjectionRegistrationTest,
142
+ "UE.MCP.Level.InstanceProjection.RegistrationAndPureValidation",
143
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
144
+
145
+ bool FLevelInstanceProjectionRegistrationTest::RunTest(const FString& Parameters)
146
+ {
147
+ FMCPHandlerRegistry Registry;
148
+ FLevelHandlers::RegisterHandlers(Registry);
149
+ TestTrue(
150
+ TEXT("native projection handler is registered"),
151
+ Registry.HasHandler(TEXT("snap_instances_to_surface")));
152
+ TestTrue(
153
+ TEXT("bounded batch handler has an explicit timeout"),
154
+ Registry.GetHandlerTimeout(TEXT("snap_instances_to_surface")) >= 300.0f);
155
+
156
+ using namespace UEMCPInstanceProjection;
157
+ EMissPolicy Policy = EMissPolicy::Error;
158
+ TestTrue(TEXT("miss policy is case insensitive"), ParseMissPolicy(TEXT(" SKIP "), Policy));
159
+ TestTrue(TEXT("skip policy is selected"), Policy == EMissPolicy::Skip);
160
+ TestFalse(TEXT("unknown miss policy is rejected"), ParseMissPolicy(TEXT("continue"), Policy));
161
+ TestTrue(
162
+ TEXT("surface offset follows the normalized impact normal"),
163
+ ApplySurfaceOffset(FVector(1.0, 2.0, 3.0), FVector(0.0, 0.0, 4.0), 7.0)
164
+ .Equals(FVector(1.0, 2.0, 10.0)));
165
+ return true;
166
+ }
167
+
168
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
169
+ FLevelInstanceProjectionDryRunAndCommitTest,
170
+ "UE.MCP.Level.InstanceProjection.DryRunBoundsAndAtomicCommit",
171
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
172
+
173
+ bool FLevelInstanceProjectionDryRunAndCommitTest::RunTest(const FString& Parameters)
174
+ {
175
+ FScopedProjectionTestWorld TestWorld;
176
+ const FString TargetLabel = TEXT("UEMCP_ProjectionTarget");
177
+ const FString SurfaceLabel = TEXT("UEMCP_ProjectionSurface");
178
+ const FString BlockerLabel = TEXT("UEMCP_ProjectionBlocker");
179
+ AActor* Target = SpawnInstanceProjectionTestActor(TestWorld.Get(), TargetLabel);
180
+ AActor* Surface = SpawnInstanceProjectionTestActor(TestWorld.Get(), SurfaceLabel);
181
+ AActor* Blocker = SpawnInstanceProjectionTestActor(TestWorld.Get(), BlockerLabel);
182
+ TestNotNull(TEXT("target actor exists"), Target);
183
+ TestNotNull(TEXT("surface actor exists"), Surface);
184
+ TestNotNull(TEXT("non-matching blocker exists"), Blocker);
185
+ if (!Target || !Surface || !Blocker) return false;
186
+
187
+ const FQuat OriginalRotation = FRotator(0.0, 37.0, 0.0).Quaternion();
188
+ const FVector OriginalScale(1.2, 0.8, 1.5);
189
+ const TArray<FTransform> OriginalTransforms = {
190
+ FTransform(OriginalRotation, FVector(0.0, 0.0, 500.0), OriginalScale),
191
+ FTransform(FQuat::Identity, FVector(1000.0, 0.0, 500.0), FVector::OneVector),
192
+ };
193
+ UInstancedStaticMeshComponent* Instances = AddInstanceComponent(Target, OriginalTransforms);
194
+ UBoxComponent* SurfaceBox = AddSurfaceBox(Surface, FVector(150.0, 150.0, 100.0));
195
+ UBoxComponent* BlockerBox = AddSurfaceBox(Blocker, FVector(150.0, 150.0, 50.0));
196
+ TestNotNull(TEXT("instance component exists"), Instances);
197
+ TestNotNull(TEXT("surface collision exists"), SurfaceBox);
198
+ TestNotNull(TEXT("blocker collision exists"), BlockerBox);
199
+ if (!Instances || !SurfaceBox || !BlockerBox) return false;
200
+ BlockerBox->SetWorldLocation(FVector(0.0, 0.0, 250.0));
201
+
202
+ TSharedPtr<FJsonObject> DryRunRequest = MakeProjectionRequest(TargetLabel, SurfaceLabel);
203
+ DryRunRequest->SetArrayField(TEXT("instanceIndices"), { MakeShared<FJsonValueNumber>(0) });
204
+ const TSharedPtr<FJsonValue> DryRunResponse =
205
+ UEMCPInstanceProjection::SnapInstancesToSurfaceInWorld(TestWorld.Get(), DryRunRequest);
206
+ TestTrue(TEXT("dry run returns an object"), DryRunResponse.IsValid() && DryRunResponse->Type == EJson::Object);
207
+ if (!DryRunResponse.IsValid() || DryRunResponse->Type != EJson::Object) return false;
208
+ const TSharedPtr<FJsonObject> DryRunResult = DryRunResponse->AsObject();
209
+ TestTrue(TEXT("dry run succeeds"), DryRunResult->GetBoolField(TEXT("success")));
210
+ TestTrue(TEXT("dry run is the default"), DryRunResult->GetBoolField(TEXT("dryRun")));
211
+ TestTrue(TEXT("dry-run preflight passes"), DryRunResult->GetBoolField(TEXT("preflightPassed")));
212
+ TestFalse(TEXT("dry run performs no mutation"), DryRunResult->GetBoolField(TEXT("mutationPerformed")));
213
+ TestEqual(TEXT("dry run reaches the filtered surface behind a blocker"), DryRunResult->GetIntegerField(TEXT("hitCount")), 1);
214
+
215
+ FTransform AfterDryRun;
216
+ TestTrue(TEXT("instance remains readable after dry run"), ReadWorldTransform(Instances, 0, AfterDryRun));
217
+ TestTrue(TEXT("dry run preserves the original transform"), AfterDryRun.Equals(OriginalTransforms[0], 0.01));
218
+
219
+ TSharedPtr<FJsonObject> BoundedRequest = MakeProjectionRequest(TargetLabel, SurfaceLabel);
220
+ BoundedRequest->SetNumberField(TEXT("maxInstances"), 1);
221
+ const TSharedPtr<FJsonValue> BoundedResponse =
222
+ UEMCPInstanceProjection::SnapInstancesToSurfaceInWorld(TestWorld.Get(), BoundedRequest);
223
+ TestFalse(TEXT("oversized request is rejected"), BoundedResponse->AsObject()->GetBoolField(TEXT("success")));
224
+ TestTrue(
225
+ TEXT("oversized rejection reports the bound"),
226
+ BoundedResponse->AsObject()->GetStringField(TEXT("error")).Contains(TEXT("maxInstances")));
227
+
228
+ TSharedPtr<FJsonObject> AtomicRequest = MakeProjectionRequest(TargetLabel, SurfaceLabel);
229
+ AtomicRequest->SetBoolField(TEXT("dryRun"), false);
230
+ AtomicRequest->SetStringField(TEXT("onMiss"), TEXT("error"));
231
+ const TSharedPtr<FJsonValue> AtomicResponse =
232
+ UEMCPInstanceProjection::SnapInstancesToSurfaceInWorld(TestWorld.Get(), AtomicRequest);
233
+ const TSharedPtr<FJsonObject> AtomicResult = AtomicResponse->AsObject();
234
+ TestFalse(TEXT("a preflight miss rejects the commit"), AtomicResult->GetBoolField(TEXT("success")));
235
+ TestFalse(TEXT("failed preflight reports no mutation"), AtomicResult->GetBoolField(TEXT("mutationPerformed")));
236
+ TestEqual(TEXT("one instance misses"), AtomicResult->GetIntegerField(TEXT("missCount")), 1);
237
+
238
+ FTransform AfterAtomicFailure;
239
+ TestTrue(TEXT("instance remains readable after rejected commit"), ReadWorldTransform(Instances, 0, AfterAtomicFailure));
240
+ TestTrue(
241
+ TEXT("onMiss=error leaves every instance unchanged"),
242
+ AfterAtomicFailure.Equals(OriginalTransforms[0], 0.01));
243
+
244
+ TSharedPtr<FJsonObject> CommitRequest = MakeProjectionRequest(TargetLabel, SurfaceLabel);
245
+ CommitRequest->SetBoolField(TEXT("dryRun"), false);
246
+ CommitRequest->SetStringField(TEXT("onMiss"), TEXT("skip"));
247
+ const TSharedPtr<FJsonValue> CommitResponse =
248
+ UEMCPInstanceProjection::SnapInstancesToSurfaceInWorld(TestWorld.Get(), CommitRequest);
249
+ const TSharedPtr<FJsonObject> CommitResult = CommitResponse->AsObject();
250
+ TestTrue(TEXT("skip commit succeeds"), CommitResult->GetBoolField(TEXT("success")));
251
+ TestTrue(TEXT("commit performs a mutation"), CommitResult->GetBoolField(TEXT("mutationPerformed")));
252
+ TestEqual(TEXT("commit updates one instance"), CommitResult->GetIntegerField(TEXT("updatedCount")), 1);
253
+
254
+ FTransform UpdatedHit;
255
+ FTransform UnchangedMiss;
256
+ TestTrue(TEXT("updated instance remains readable"), ReadWorldTransform(Instances, 0, UpdatedHit));
257
+ TestTrue(TEXT("skipped instance remains readable"), ReadWorldTransform(Instances, 1, UnchangedMiss));
258
+ TestTrue(TEXT("projected location uses the surface offset"), FMath::IsNearlyEqual(UpdatedHit.GetLocation().Z, 110.0, 0.1));
259
+ TestTrue(TEXT("projection preserves rotation"), UpdatedHit.GetRotation().Equals(OriginalRotation, 0.001));
260
+ TestTrue(TEXT("projection preserves scale"), UpdatedHit.GetScale3D().Equals(OriginalScale, 0.001));
261
+ TestTrue(TEXT("onMiss=skip leaves the miss unchanged"), UnchangedMiss.Equals(OriginalTransforms[1], 0.01));
262
+ return true;
263
+ }
264
+
265
+ #endif
@@ -0,0 +1,258 @@
1
+ // Coverage for post-process settings and their override flags (#950).
2
+ //
3
+ // The bug this guards is silent by construction: a value written into
4
+ // FPostProcessSettings without its bOverride_<Name> bit reads back exactly as
5
+ // requested and is ignored by the renderer. So the assertion that matters is
6
+ // not "the value was written" but "the bit was turned on with it", and it is
7
+ // checked by reading the struct back through the reader rather than by trusting
8
+ // the writer's own report.
9
+ //
10
+ // The round trip runs against a transient verification actor, which cannot be
11
+ // saved into the map and is destroyed at the end.
12
+
13
+ #if WITH_DEV_AUTOMATION_TESTS
14
+
15
+ #include "Engine/Scene.h"
16
+ #include "HandlerRegistry.h"
17
+ #include "Handlers/LevelHandlers.h"
18
+ #include "Misc/AutomationTest.h"
19
+ #include "UObject/UnrealType.h"
20
+
21
+ namespace
22
+ {
23
+ TSharedPtr<FJsonObject> MCPPostProcessTestRun(
24
+ FMCPHandlerRegistry& Registry,
25
+ const TCHAR* Method,
26
+ const TSharedPtr<FJsonObject>& Request)
27
+ {
28
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
29
+ if (!Response.IsValid() || Response->Type != EJson::Object)
30
+ {
31
+ return nullptr;
32
+ }
33
+ return Response->AsObject();
34
+ }
35
+
36
+ /** The row for one setting in a get_post_process_settings response. */
37
+ TSharedPtr<FJsonObject> MCPPostProcessTestFindRow(
38
+ const TSharedPtr<FJsonObject>& Response,
39
+ const TCHAR* SettingName)
40
+ {
41
+ if (!Response.IsValid()) return nullptr;
42
+ const TArray<TSharedPtr<FJsonValue>>* Rows = nullptr;
43
+ if (!Response->TryGetArrayField(TEXT("settings"), Rows) || !Rows) return nullptr;
44
+ for (const TSharedPtr<FJsonValue>& Value : *Rows)
45
+ {
46
+ const TSharedPtr<FJsonObject> Row = Value.IsValid() ? Value->AsObject() : nullptr;
47
+ if (!Row.IsValid()) continue;
48
+ if (Row->GetStringField(TEXT("name")).Equals(SettingName, ESearchCase::IgnoreCase)) return Row;
49
+ }
50
+ return nullptr;
51
+ }
52
+ }
53
+
54
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
55
+ FLevelPostProcessOverridePairingTest,
56
+ "UE.MCP.Level.PostProcess.EveryExposureValueHasAnOverrideBit",
57
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
58
+
59
+ bool FLevelPostProcessOverridePairingTest::RunTest(const FString& Parameters)
60
+ {
61
+ // The whole feature rests on one naming convention: the bit that gates the
62
+ // value `Foo` is called `bOverride_Foo`. If the engine ever renames one of
63
+ // these, working generically off the reflected fields would silently stop
64
+ // enabling anything, so the convention is asserted rather than assumed.
65
+ UScriptStruct* Struct = FPostProcessSettings::StaticStruct();
66
+ TestNotNull(TEXT("FPostProcessSettings is reflected"), Struct);
67
+ if (!Struct) return true;
68
+
69
+ for (const TCHAR* ValueField : {
70
+ TEXT("AutoExposureMinBrightness"),
71
+ TEXT("AutoExposureMaxBrightness"),
72
+ TEXT("AutoExposureBias"),
73
+ TEXT("AutoExposureMethod"),
74
+ TEXT("BloomIntensity") })
75
+ {
76
+ FProperty* Value = Struct->FindPropertyByName(FName(ValueField));
77
+ TestNotNull(FString::Printf(TEXT("%s exists"), ValueField), Value);
78
+
79
+ const FString FlagName = FString(TEXT("bOverride_")) + ValueField;
80
+ FBoolProperty* Flag = CastField<FBoolProperty>(Struct->FindPropertyByName(FName(*FlagName)));
81
+ TestNotNull(FString::Printf(TEXT("%s exists and is a bool"), *FlagName), Flag);
82
+ }
83
+
84
+ return true;
85
+ }
86
+
87
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
88
+ FLevelPostProcessSettingsRoundTripTest,
89
+ "UE.MCP.Level.PostProcess.SettingWritesTheOverrideBitToo",
90
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
91
+
92
+ bool FLevelPostProcessSettingsRoundTripTest::RunTest(const FString& Parameters)
93
+ {
94
+ FMCPHandlerRegistry Registry;
95
+ FLevelHandlers::RegisterHandlers(Registry);
96
+
97
+ for (const TCHAR* Method : {
98
+ TEXT("set_post_process_settings"),
99
+ TEXT("get_post_process_settings"),
100
+ TEXT("set_fixed_exposure") })
101
+ {
102
+ TestTrue(FString::Printf(TEXT("%s is registered"), Method), Registry.HasHandler(Method));
103
+ }
104
+
105
+ // A settings map is required: an empty write is a request that means
106
+ // nothing, and answering it with success would be a lie.
107
+ {
108
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
109
+ Request->SetStringField(TEXT("actorLabel"), TEXT("UEMCP_NoSuchPostProcessVolume"));
110
+ const TSharedPtr<FJsonObject> Response =
111
+ MCPPostProcessTestRun(Registry, TEXT("set_post_process_settings"), Request);
112
+ if (Response.IsValid())
113
+ {
114
+ TestFalse(TEXT("a write with no settings fails"), Response->GetBoolField(TEXT("success")));
115
+ }
116
+ }
117
+
118
+ // The round trip needs a subject. A transient verification actor is one
119
+ // that cannot end up committed to source control.
120
+ TSharedPtr<FJsonObject> SpawnRequest = MakeShared<FJsonObject>();
121
+ SpawnRequest->SetStringField(TEXT("actorClass"), TEXT("PostProcessVolume"));
122
+ SpawnRequest->SetStringField(TEXT("label"), TEXT("UEMCP_PostProcessOverrideTest"));
123
+ const TSharedPtr<FJsonObject> SpawnResponse =
124
+ MCPPostProcessTestRun(Registry, TEXT("spawn_transient_actor"), SpawnRequest);
125
+ if (!SpawnResponse.IsValid() || !SpawnResponse->GetBoolField(TEXT("success")))
126
+ {
127
+ // No editor world in this context, and this test may not open a map.
128
+ return true;
129
+ }
130
+ const TSharedPtr<FJsonObject> SpawnedActor = SpawnResponse->GetObjectField(TEXT("actor"));
131
+ const FString ActorPath = SpawnedActor.IsValid() ? SpawnedActor->GetStringField(TEXT("actorPath")) : FString();
132
+ const FString ActorLabel = TEXT("UEMCP_PostProcessOverrideTest");
133
+
134
+ // An unknown key fails the WHOLE call rather than writing the keys around
135
+ // it, so a typo cannot leave a volume half-configured.
136
+ {
137
+ TSharedPtr<FJsonObject> Settings = MakeShared<FJsonObject>();
138
+ Settings->SetNumberField(TEXT("AutoExposureMinBrightness"), 2.0);
139
+ Settings->SetNumberField(TEXT("NotARealPostProcessSetting"), 1.0);
140
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
141
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
142
+ Request->SetObjectField(TEXT("settings"), Settings);
143
+ const TSharedPtr<FJsonObject> Response =
144
+ MCPPostProcessTestRun(Registry, TEXT("set_post_process_settings"), Request);
145
+ if (Response.IsValid())
146
+ {
147
+ TestFalse(TEXT("an unknown setting name fails the call"), Response->GetBoolField(TEXT("success")));
148
+ TestTrue(TEXT("and says nothing was written"),
149
+ Response->GetStringField(TEXT("error")).Contains(TEXT("Nothing was written")));
150
+ }
151
+ }
152
+
153
+ // The assertion the issue is about: writing the value also turns the bit on.
154
+ {
155
+ TSharedPtr<FJsonObject> Settings = MakeShared<FJsonObject>();
156
+ Settings->SetNumberField(TEXT("AutoExposureMinBrightness"), 1.5);
157
+ Settings->SetNumberField(TEXT("AutoExposureMaxBrightness"), 1.5);
158
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
159
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
160
+ Request->SetObjectField(TEXT("settings"), Settings);
161
+ const TSharedPtr<FJsonObject> Response =
162
+ MCPPostProcessTestRun(Registry, TEXT("set_post_process_settings"), Request);
163
+ TestTrue(TEXT("the write succeeds"),
164
+ Response.IsValid() && Response->GetBoolField(TEXT("success")));
165
+ }
166
+
167
+ // Read it back through the reader rather than trusting the writer's report.
168
+ {
169
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
170
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
171
+ Request->SetBoolField(TEXT("onlyOverridden"), true);
172
+ const TSharedPtr<FJsonObject> Response =
173
+ MCPPostProcessTestRun(Registry, TEXT("get_post_process_settings"), Request);
174
+ TestTrue(TEXT("the read succeeds"),
175
+ Response.IsValid() && Response->GetBoolField(TEXT("success")));
176
+
177
+ const TSharedPtr<FJsonObject> MinRow =
178
+ MCPPostProcessTestFindRow(Response, TEXT("AutoExposureMinBrightness"));
179
+ TestNotNull(TEXT("min brightness is reported as overridden"), MinRow.Get());
180
+ if (MinRow.IsValid())
181
+ {
182
+ TestTrue(TEXT("and its override bit really is on"), MinRow->GetBoolField(TEXT("overridden")));
183
+ }
184
+
185
+ const TSharedPtr<FJsonObject> MaxRow =
186
+ MCPPostProcessTestFindRow(Response, TEXT("AutoExposureMaxBrightness"));
187
+ TestNotNull(TEXT("max brightness is reported as overridden"), MaxRow.Get());
188
+ }
189
+
190
+ // enableOverrides=false is the explicit opt out, and it must NOT turn the
191
+ // bit on, or the opt out would be a lie in the other direction.
192
+ {
193
+ TSharedPtr<FJsonObject> Settings = MakeShared<FJsonObject>();
194
+ Settings->SetNumberField(TEXT("BloomIntensity"), 0.25);
195
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
196
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
197
+ Request->SetObjectField(TEXT("settings"), Settings);
198
+ Request->SetBoolField(TEXT("enableOverrides"), false);
199
+ const TSharedPtr<FJsonObject> Response =
200
+ MCPPostProcessTestRun(Registry, TEXT("set_post_process_settings"), Request);
201
+ if (Response.IsValid() && Response->GetBoolField(TEXT("success")))
202
+ {
203
+ TSharedPtr<FJsonObject> ReadRequest = MakeShared<FJsonObject>();
204
+ ReadRequest->SetStringField(TEXT("actorLabel"), ActorLabel);
205
+ TArray<TSharedPtr<FJsonValue>> Names;
206
+ Names.Add(MakeShared<FJsonValueString>(TEXT("BloomIntensity")));
207
+ ReadRequest->SetArrayField(TEXT("names"), Names);
208
+ const TSharedPtr<FJsonObject> ReadResponse =
209
+ MCPPostProcessTestRun(Registry, TEXT("get_post_process_settings"), ReadRequest);
210
+ const TSharedPtr<FJsonObject> Row =
211
+ MCPPostProcessTestFindRow(ReadResponse, TEXT("BloomIntensity"));
212
+ if (Row.IsValid())
213
+ {
214
+ TestFalse(TEXT("enableOverrides=false leaves the bit off"), Row->GetBoolField(TEXT("overridden")));
215
+ }
216
+ }
217
+ }
218
+
219
+ // set_fixed_exposure is the same mechanism with the two writes chosen for
220
+ // you, so it has to leave both bits on as well.
221
+ {
222
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
223
+ Request->SetStringField(TEXT("actorLabel"), ActorLabel);
224
+ Request->SetNumberField(TEXT("exposure"), 3.0);
225
+ const TSharedPtr<FJsonObject> Response =
226
+ MCPPostProcessTestRun(Registry, TEXT("set_fixed_exposure"), Request);
227
+ TestTrue(TEXT("set_fixed_exposure succeeds"),
228
+ Response.IsValid() && Response->GetBoolField(TEXT("success")));
229
+
230
+ TSharedPtr<FJsonObject> ReadRequest = MakeShared<FJsonObject>();
231
+ ReadRequest->SetStringField(TEXT("actorLabel"), ActorLabel);
232
+ ReadRequest->SetBoolField(TEXT("onlyOverridden"), true);
233
+ const TSharedPtr<FJsonObject> ReadResponse =
234
+ MCPPostProcessTestRun(Registry, TEXT("get_post_process_settings"), ReadRequest);
235
+ const TSharedPtr<FJsonObject> MinRow =
236
+ MCPPostProcessTestFindRow(ReadResponse, TEXT("AutoExposureMinBrightness"));
237
+ const TSharedPtr<FJsonObject> MaxRow =
238
+ MCPPostProcessTestFindRow(ReadResponse, TEXT("AutoExposureMaxBrightness"));
239
+ TestNotNull(TEXT("fixed exposure overrode min brightness"), MinRow.Get());
240
+ TestNotNull(TEXT("fixed exposure overrode max brightness"), MaxRow.Get());
241
+ }
242
+
243
+ // Clean up the subject, since a test about not leaving state behind that
244
+ // left an actor behind would be absurd.
245
+ if (!ActorPath.IsEmpty())
246
+ {
247
+ TSharedPtr<FJsonObject> DestroyRequest = MakeShared<FJsonObject>();
248
+ DestroyRequest->SetStringField(TEXT("actorPath"), ActorPath);
249
+ const TSharedPtr<FJsonObject> DestroyResponse =
250
+ MCPPostProcessTestRun(Registry, TEXT("destroy_transient_actor"), DestroyRequest);
251
+ TestTrue(TEXT("the verification volume is destroyed"),
252
+ DestroyResponse.IsValid() && DestroyResponse->GetBoolField(TEXT("success")));
253
+ }
254
+
255
+ return true;
256
+ }
257
+
258
+ #endif // WITH_DEV_AUTOMATION_TESTS