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,527 @@
1
+ // Post-process settings, with their override flags (#950).
2
+ //
3
+ // FPostProcessSettings is two parallel halves: a value for every setting, and a
4
+ // `bOverride_<Name>` bit that decides whether the renderer reads that value at
5
+ // all. Writing AutoExposureMinBrightness through the generic property setter
6
+ // stores the number and leaves the bit off, so the details panel shows the
7
+ // value the caller asked for while the engine keeps using the default. Nothing
8
+ // errors, nothing looks wrong, and the setting simply does not apply. That trap
9
+ // costs hours every time someone falls into it.
10
+ //
11
+ // So the setter here owns both halves: for every key it writes, it also enables
12
+ // the matching override bit. That auto-enable IS the action; a setter that
13
+ // wrote the value and left the flag off would reproduce the bug it exists to
14
+ // close. The reader is the other half of the answer: it says which settings a
15
+ // volume is actually overriding, without dumping the whole struct as one
16
+ // ExportText blob for the caller to parse.
17
+ //
18
+ // The work is done against the struct's REFLECTED fields, so every setting in
19
+ // FPostProcessSettings is covered, on this engine version and the next one. No
20
+ // hardcoded exposure list.
21
+ //
22
+ // Translation-unit partition of FLevelHandlers - registration lives in
23
+ // LevelHandlers.cpp::RegisterHandlers.
24
+
25
+ #include "LevelHandlers.h"
26
+ #include "HandlerRegistry.h"
27
+ #include "HandlerUtils.h"
28
+ #include "HandlerJsonProperty.h"
29
+
30
+ #include "Editor.h"
31
+ #include "Engine/World.h"
32
+ #include "Engine/Scene.h"
33
+ #include "EngineUtils.h"
34
+ #include "GameFramework/Actor.h"
35
+ #include "Components/ActorComponent.h"
36
+ #include "UObject/UnrealType.h"
37
+ #include "Dom/JsonObject.h"
38
+ #include "Dom/JsonValue.h"
39
+
40
+ namespace
41
+ {
42
+ /** The prefix every FPostProcessSettings override bit carries. */
43
+ const TCHAR* const MCPPostProcessOverridePrefix = TEXT("bOverride_");
44
+
45
+ /**
46
+ * One FPostProcessSettings living on an actor or on one of its components,
47
+ * with everything needed to read it, write it and name it back to the caller.
48
+ */
49
+ struct FMCPPostProcessTarget
50
+ {
51
+ UObject* Owner = nullptr;
52
+ FStructProperty* StructProp = nullptr;
53
+ void* StructPtr = nullptr;
54
+ FString OwnerKind; // "actor" or "component"
55
+ FString OwnerName; // the component's name, or the actor's label
56
+ FString PropertyName; // "Settings", "PostProcessSettings", ...
57
+
58
+ bool IsValid() const { return Owner != nullptr && StructProp != nullptr && StructPtr != nullptr; }
59
+ FString Describe() const { return FString::Printf(TEXT("%s.%s"), *OwnerName, *PropertyName); }
60
+ };
61
+
62
+ /** Append every FPostProcessSettings struct property declared on one object. */
63
+ void CollectPostProcessStructs(
64
+ UObject* Object,
65
+ const TCHAR* OwnerKind,
66
+ const FString& OwnerName,
67
+ TArray<FMCPPostProcessTarget>& OutTargets)
68
+ {
69
+ if (!Object) return;
70
+ UScriptStruct* WantedStruct = FPostProcessSettings::StaticStruct();
71
+ for (TFieldIterator<FStructProperty> It(Object->GetClass()); It; ++It)
72
+ {
73
+ FStructProperty* Prop = *It;
74
+ if (!Prop || Prop->Struct != WantedStruct) continue;
75
+ FMCPPostProcessTarget Target;
76
+ Target.Owner = Object;
77
+ Target.StructProp = Prop;
78
+ Target.StructPtr = Prop->ContainerPtrToValuePtr<void>(Object);
79
+ Target.OwnerKind = OwnerKind;
80
+ Target.OwnerName = OwnerName;
81
+ Target.PropertyName = Prop->GetName();
82
+ OutTargets.Add(Target);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Find the one FPostProcessSettings a request means. A PostProcessVolume has
88
+ * exactly one and needs no disambiguation; a camera actor carries one per
89
+ * camera component, so componentName / propertyName narrow it. Ambiguity is
90
+ * reported by listing the candidates rather than resolved by picking first,
91
+ * because picking first is how a write lands on the wrong camera.
92
+ */
93
+ bool ResolvePostProcessTarget(
94
+ AActor* Actor,
95
+ const TSharedPtr<FJsonObject>& Params,
96
+ FMCPPostProcessTarget& OutTarget,
97
+ FString& OutError)
98
+ {
99
+ const FString ComponentName = OptionalString(Params, TEXT("componentName"));
100
+ const FString PropertyName = OptionalString(Params, TEXT("propertyName"));
101
+
102
+ TArray<FMCPPostProcessTarget> Targets;
103
+ if (ComponentName.IsEmpty())
104
+ {
105
+ CollectPostProcessStructs(Actor, TEXT("actor"), Actor->GetActorLabel(), Targets);
106
+ }
107
+
108
+ TArray<UActorComponent*> Components;
109
+ Actor->GetComponents(Components);
110
+ for (UActorComponent* Component : Components)
111
+ {
112
+ if (!Component) continue;
113
+ if (!ComponentName.IsEmpty() && !Component->GetName().Equals(ComponentName, ESearchCase::IgnoreCase)) continue;
114
+ CollectPostProcessStructs(Component, TEXT("component"), Component->GetName(), Targets);
115
+ }
116
+
117
+ if (!PropertyName.IsEmpty())
118
+ {
119
+ Targets.RemoveAll([&PropertyName](const FMCPPostProcessTarget& T)
120
+ {
121
+ return !T.PropertyName.Equals(PropertyName, ESearchCase::IgnoreCase);
122
+ });
123
+ }
124
+
125
+ if (Targets.Num() == 0)
126
+ {
127
+ OutError = FString::Printf(
128
+ TEXT("'%s' (%s) has no FPostProcessSettings%s. PostProcessVolume carries one as 'Settings'; a camera carries one per camera component as 'PostProcessSettings'."),
129
+ *Actor->GetActorLabel(), *Actor->GetClass()->GetName(),
130
+ (ComponentName.IsEmpty() && PropertyName.IsEmpty())
131
+ ? TEXT("")
132
+ : TEXT(" matching componentName/propertyName"));
133
+ return false;
134
+ }
135
+ if (Targets.Num() > 1)
136
+ {
137
+ TArray<FString> Names;
138
+ for (const FMCPPostProcessTarget& T : Targets) Names.Add(T.Describe());
139
+ OutError = FString::Printf(
140
+ TEXT("'%s' has %d post-process settings structs; pass componentName and/or propertyName to choose. Candidates: [%s]"),
141
+ *Actor->GetActorLabel(), Targets.Num(), *FString::Join(Names, TEXT(", ")));
142
+ return false;
143
+ }
144
+
145
+ OutTarget = Targets[0];
146
+ return true;
147
+ }
148
+
149
+ /** Case-insensitive field lookup inside FPostProcessSettings. */
150
+ FProperty* FindPostProcessField(const FString& Name)
151
+ {
152
+ UStruct* Struct = FPostProcessSettings::StaticStruct();
153
+ for (TFieldIterator<FProperty> It(Struct); It; ++It)
154
+ {
155
+ FProperty* Prop = *It;
156
+ if (Prop && Prop->GetName().Equals(Name, ESearchCase::IgnoreCase)) return Prop;
157
+ }
158
+ return nullptr;
159
+ }
160
+
161
+ /** The `bOverride_<Name>` bit for a value field, or null when it has none. */
162
+ FBoolProperty* FindPostProcessOverrideFlag(const FString& ValueFieldName)
163
+ {
164
+ FProperty* Flag = FindPostProcessField(FString(MCPPostProcessOverridePrefix) + ValueFieldName);
165
+ return CastField<FBoolProperty>(Flag);
166
+ }
167
+
168
+ bool IsPostProcessOverrideFlag(const FProperty* Prop)
169
+ {
170
+ return Prop != nullptr && Prop->GetName().StartsWith(MCPPostProcessOverridePrefix);
171
+ }
172
+
173
+ // The override bits are one-bit bitfields packed together, so they are read
174
+ // and written through FBoolProperty rather than as bytes.
175
+ bool ReadPostProcessOverrideFlag(const FBoolProperty* Flag, const void* StructPtr)
176
+ {
177
+ if (!Flag || !StructPtr) return false;
178
+ return Flag->GetPropertyValue_InContainer(StructPtr);
179
+ }
180
+
181
+ void WritePostProcessOverrideFlag(FBoolProperty* Flag, void* StructPtr, bool bValue)
182
+ {
183
+ if (!Flag || !StructPtr) return;
184
+ Flag->SetPropertyValue_InContainer(StructPtr, bValue);
185
+ }
186
+
187
+ /** Nearest field names, for the "no such setting" error. */
188
+ FString SuggestPostProcessFields(const FString& Wanted)
189
+ {
190
+ TArray<FString> Near;
191
+ for (TFieldIterator<FProperty> It(FPostProcessSettings::StaticStruct()); It; ++It)
192
+ {
193
+ FProperty* Prop = *It;
194
+ if (!Prop || IsPostProcessOverrideFlag(Prop)) continue;
195
+ const FString Name = Prop->GetName();
196
+ if (Name.Contains(Wanted, ESearchCase::IgnoreCase) || Wanted.Contains(Name, ESearchCase::IgnoreCase))
197
+ {
198
+ Near.Add(Name);
199
+ if (Near.Num() >= 8) break;
200
+ }
201
+ }
202
+ return Near.Num() > 0 ? FString::Join(Near, TEXT(", ")) : FString(TEXT("(no similar field names)"));
203
+ }
204
+
205
+ /** Resolve actorLabel or actorPath to an actor plus its post-process
206
+ * struct, or fail. #983: a label naming several actors refuses here
207
+ * rather than writing exposure onto one of them at random. */
208
+ bool ResolveActorAndPostProcessTarget(
209
+ UWorld* World,
210
+ const TSharedPtr<FJsonObject>& Params,
211
+ AActor*& OutActor,
212
+ FMCPPostProcessTarget& OutTarget,
213
+ TSharedPtr<FJsonValue>& OutError)
214
+ {
215
+ // Presence check only, so a call with no selector at all fails naming
216
+ // both parameters before the world is touched. The value is discarded:
217
+ // the resolver reads the parameters itself, and every message below
218
+ // names the actor that actually answered.
219
+ FString UnusedSelector;
220
+ if (TSharedPtr<FJsonValue> Err = RequireStringAlt(Params, TEXT("actorLabel"), TEXT("actorPath"), UnusedSelector))
221
+ {
222
+ OutError = Err;
223
+ return false;
224
+ }
225
+ OutActor = MCPResolveActor(World, Params, OutError);
226
+ if (!OutActor) return false;
227
+ FString ResolveError;
228
+ if (!ResolvePostProcessTarget(OutActor, Params, OutTarget, ResolveError))
229
+ {
230
+ OutError = MCPError(ResolveError);
231
+ return false;
232
+ }
233
+ return true;
234
+ }
235
+
236
+ /** Describe the resolved struct on a response, so a caller sees what it hit. */
237
+ void EmitPostProcessTargetFields(const TSharedPtr<FJsonObject>& Result, AActor* Actor, const FMCPPostProcessTarget& Target)
238
+ {
239
+ Result->SetStringField(TEXT("actorLabel"), Actor->GetActorLabel());
240
+ Result->SetStringField(TEXT("actorPath"), Actor->GetPathName());
241
+ Result->SetStringField(TEXT("actorClass"), Actor->GetClass()->GetName());
242
+ Result->SetStringField(TEXT("settingsOwner"), Target.OwnerKind);
243
+ Result->SetStringField(TEXT("settingsOwnerName"), Target.OwnerName);
244
+ Result->SetStringField(TEXT("settingsProperty"), Target.PropertyName);
245
+ }
246
+
247
+ /**
248
+ * Write one setting and turn its override bit on. Returns the per-key report
249
+ * either way, so a partial refusal is legible rather than silent.
250
+ */
251
+ TSharedPtr<FJsonObject> ApplyPostProcessSetting(
252
+ const FMCPPostProcessTarget& Target,
253
+ const FString& Key,
254
+ const TSharedPtr<FJsonValue>& Value,
255
+ bool bEnableOverride,
256
+ bool& bOutOk)
257
+ {
258
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
259
+ Entry->SetStringField(TEXT("name"), Key);
260
+ bOutOk = false;
261
+
262
+ FProperty* Prop = FindPostProcessField(Key);
263
+ if (!Prop)
264
+ {
265
+ Entry->SetBoolField(TEXT("applied"), false);
266
+ Entry->SetStringField(TEXT("error"), FString::Printf(
267
+ TEXT("FPostProcessSettings has no field '%s'. Similar: %s"), *Key, *SuggestPostProcessFields(Key)));
268
+ return Entry;
269
+ }
270
+
271
+ Entry->SetStringField(TEXT("resolvedName"), Prop->GetName());
272
+ Entry->SetField(TEXT("previousValue"), MCPExportPropertyValue(Prop, Target.StructPtr));
273
+
274
+ void* ValueAddr = Prop->ContainerPtrToValuePtr<void>(Target.StructPtr);
275
+ FString SetError;
276
+ if (!MCPJsonProperty::SetJsonOnProperty(Prop, ValueAddr, Value, SetError))
277
+ {
278
+ Entry->SetBoolField(TEXT("applied"), false);
279
+ Entry->SetStringField(TEXT("error"), SetError);
280
+ return Entry;
281
+ }
282
+ Entry->SetBoolField(TEXT("applied"), true);
283
+ Entry->SetField(TEXT("newValue"), MCPExportPropertyValue(Prop, Target.StructPtr));
284
+ bOutOk = true;
285
+
286
+ // The whole point: the value the caller just wrote is invisible to the
287
+ // renderer until this bit is on. A key that IS an override bit is its own
288
+ // flag and needs nothing further.
289
+ if (IsPostProcessOverrideFlag(Prop))
290
+ {
291
+ Entry->SetBoolField(TEXT("isOverrideFlag"), true);
292
+ return Entry;
293
+ }
294
+
295
+ FBoolProperty* Flag = FindPostProcessOverrideFlag(Prop->GetName());
296
+ if (!Flag)
297
+ {
298
+ // A handful of fields (the blendable array, for one) have no override
299
+ // bit at all. Say so rather than implying one was enabled.
300
+ Entry->SetBoolField(TEXT("hasOverrideFlag"), false);
301
+ return Entry;
302
+ }
303
+ Entry->SetBoolField(TEXT("hasOverrideFlag"), true);
304
+ Entry->SetStringField(TEXT("overrideFlag"), Flag->GetName());
305
+ Entry->SetBoolField(TEXT("overrideWasEnabled"), ReadPostProcessOverrideFlag(Flag, Target.StructPtr));
306
+ if (bEnableOverride)
307
+ {
308
+ WritePostProcessOverrideFlag(Flag, Target.StructPtr, true);
309
+ }
310
+ // Read back rather than echo: the response says what the struct holds now.
311
+ Entry->SetBoolField(TEXT("overrideEnabled"), ReadPostProcessOverrideFlag(Flag, Target.StructPtr));
312
+ return Entry;
313
+ }
314
+ }
315
+
316
+
317
+ // level(set_post_process_settings): write settings AND enable their override
318
+ // bits, which is the difference between a value that applies and a value that
319
+ // sits in the details panel while the engine ignores it.
320
+ TSharedPtr<FJsonValue> FLevelHandlers::SetPostProcessSettings(const TSharedPtr<FJsonObject>& Params)
321
+ {
322
+ REQUIRE_EDITOR_WORLD(World);
323
+
324
+ AActor* Actor = nullptr;
325
+ FMCPPostProcessTarget Target;
326
+ TSharedPtr<FJsonValue> ResolveError;
327
+ if (!ResolveActorAndPostProcessTarget(World, Params, Actor, Target, ResolveError)) return ResolveError;
328
+
329
+ const TSharedPtr<FJsonObject>* SettingsObj = nullptr;
330
+ if (!Params->TryGetObjectField(TEXT("settings"), SettingsObj) || !SettingsObj || !SettingsObj->IsValid())
331
+ {
332
+ return MCPError(TEXT("Missing 'settings' object of settingName -> value, e.g. {\"AutoExposureMinBrightness\": 1.0, \"AutoExposureMaxBrightness\": 1.0}"));
333
+ }
334
+
335
+ // Resolve every key before writing any of them, so a typo fails the call
336
+ // instead of half-applying it and leaving the volume in a state nobody
337
+ // asked for.
338
+ TArray<TPair<FString, TSharedPtr<FJsonValue>>> Requested;
339
+ TArray<FString> Unknown;
340
+ for (const auto& Pair : (*SettingsObj)->Values)
341
+ {
342
+ // FJsonObject::Values is keyed by a shared string on 5.8, so the key is
343
+ // materialised rather than bound as an FString. Same idiom as the rest
344
+ // of the module.
345
+ const FString Key = FString(*Pair.Key);
346
+ if (!FindPostProcessField(Key)) Unknown.Add(Key);
347
+ Requested.Add(TPair<FString, TSharedPtr<FJsonValue>>(Key, Pair.Value));
348
+ }
349
+ if (Requested.Num() == 0)
350
+ {
351
+ return MCPError(TEXT("'settings' is empty; pass at least one settingName -> value pair"));
352
+ }
353
+ if (Unknown.Num() > 0)
354
+ {
355
+ return MCPError(FString::Printf(
356
+ TEXT("FPostProcessSettings has no field named %s. Nothing was written. Use level(get_post_process_settings) to list the exact names."),
357
+ *FString::Join(Unknown, TEXT(", "))));
358
+ }
359
+
360
+ const bool bEnableOverrides = OptionalBool(Params, TEXT("enableOverrides"), true);
361
+
362
+ Target.Owner->Modify();
363
+ TArray<TSharedPtr<FJsonValue>> Applied;
364
+ int32 SuccessCount = 0;
365
+ int32 FailureCount = 0;
366
+ for (const TPair<FString, TSharedPtr<FJsonValue>>& Pair : Requested)
367
+ {
368
+ bool bOk = false;
369
+ Applied.Add(MakeShared<FJsonValueObject>(
370
+ ApplyPostProcessSetting(Target, Pair.Key, Pair.Value, bEnableOverrides, bOk)));
371
+ bOk ? ++SuccessCount : ++FailureCount;
372
+ }
373
+ Target.Owner->PostEditChange();
374
+ Actor->MarkPackageDirty();
375
+
376
+ auto Result = MCPSuccess();
377
+ MCPSetUpdated(Result);
378
+ EmitPostProcessTargetFields(Result, Actor, Target);
379
+ Result->SetBoolField(TEXT("enableOverrides"), bEnableOverrides);
380
+ Result->SetNumberField(TEXT("appliedCount"), SuccessCount);
381
+ Result->SetNumberField(TEXT("failedCount"), FailureCount);
382
+ Result->SetArrayField(TEXT("settings"), Applied);
383
+ if (FailureCount > 0) Result->SetBoolField(TEXT("success"), false);
384
+ Result->SetStringField(TEXT("note"),
385
+ TEXT("Each written setting also had its bOverride_<Name> flag enabled, which is what makes the value take effect. The level is left dirty and is NOT saved."));
386
+ return MCPResult(Result);
387
+ }
388
+
389
+
390
+ // level(get_post_process_settings): what this volume is actually overriding.
391
+ // onlyOverridden answers "is exposure being overridden here" without dumping
392
+ // FPostProcessSettings as one ExportText blob for the caller to parse.
393
+ TSharedPtr<FJsonValue> FLevelHandlers::GetPostProcessSettings(const TSharedPtr<FJsonObject>& Params)
394
+ {
395
+ REQUIRE_EDITOR_WORLD(World);
396
+
397
+ AActor* Actor = nullptr;
398
+ FMCPPostProcessTarget Target;
399
+ TSharedPtr<FJsonValue> ResolveError;
400
+ if (!ResolveActorAndPostProcessTarget(World, Params, Actor, Target, ResolveError)) return ResolveError;
401
+
402
+ const bool bOnlyOverridden = OptionalBool(Params, TEXT("onlyOverridden"), false);
403
+ const FString NameFilter = OptionalString(Params, TEXT("nameContains"));
404
+
405
+ TArray<FString> WantedNames;
406
+ const TArray<TSharedPtr<FJsonValue>>* NamesArr = nullptr;
407
+ if (Params->TryGetArrayField(TEXT("names"), NamesArr) && NamesArr)
408
+ {
409
+ for (const TSharedPtr<FJsonValue>& V : *NamesArr)
410
+ {
411
+ FString Name;
412
+ if (V.IsValid() && V->TryGetString(Name) && !Name.IsEmpty()) WantedNames.Add(Name);
413
+ }
414
+ }
415
+
416
+ TArray<TSharedPtr<FJsonValue>> Rows;
417
+ int32 TotalFields = 0;
418
+ int32 OverriddenCount = 0;
419
+ for (TFieldIterator<FProperty> It(FPostProcessSettings::StaticStruct()); It; ++It)
420
+ {
421
+ FProperty* Prop = *It;
422
+ if (!Prop) continue;
423
+ // The override bits are reported as the `overridden` flag on the value
424
+ // they gate, not as settings of their own. Listing both would double the
425
+ // payload and invite a caller to write the bit without the value.
426
+ if (IsPostProcessOverrideFlag(Prop)) continue;
427
+
428
+ const FString Name = Prop->GetName();
429
+ ++TotalFields;
430
+
431
+ FBoolProperty* Flag = FindPostProcessOverrideFlag(Name);
432
+ const bool bOverridden = Flag != nullptr && ReadPostProcessOverrideFlag(Flag, Target.StructPtr);
433
+ if (bOverridden) ++OverriddenCount;
434
+
435
+ if (bOnlyOverridden && !bOverridden) continue;
436
+ if (!NameFilter.IsEmpty() && !Name.Contains(NameFilter, ESearchCase::IgnoreCase)) continue;
437
+ if (WantedNames.Num() > 0)
438
+ {
439
+ const bool bWanted = WantedNames.ContainsByPredicate([&Name](const FString& Candidate)
440
+ {
441
+ return Candidate.Equals(Name, ESearchCase::IgnoreCase);
442
+ });
443
+ if (!bWanted) continue;
444
+ }
445
+
446
+ TSharedPtr<FJsonObject> Row = MakeShared<FJsonObject>();
447
+ Row->SetStringField(TEXT("name"), Name);
448
+ Row->SetStringField(TEXT("type"), Prop->GetCPPType());
449
+ Row->SetField(TEXT("value"), MCPExportPropertyValue(Prop, Target.StructPtr));
450
+ Row->SetBoolField(TEXT("hasOverrideFlag"), Flag != nullptr);
451
+ if (Flag)
452
+ {
453
+ Row->SetStringField(TEXT("overrideFlag"), Flag->GetName());
454
+ }
455
+ Row->SetBoolField(TEXT("overridden"), bOverridden);
456
+ Rows.Add(MakeShared<FJsonValueObject>(Row));
457
+ }
458
+
459
+ auto Result = MCPSuccess();
460
+ EmitPostProcessTargetFields(Result, Actor, Target);
461
+ Result->SetBoolField(TEXT("onlyOverridden"), bOnlyOverridden);
462
+ Result->SetNumberField(TEXT("totalSettings"), TotalFields);
463
+ Result->SetNumberField(TEXT("overriddenCount"), OverriddenCount);
464
+ Result->SetNumberField(TEXT("returnedCount"), Rows.Num());
465
+ Result->SetArrayField(TEXT("settings"), Rows);
466
+ Result->SetStringField(TEXT("note"),
467
+ TEXT("overridden is the bOverride_<Name> bit: a value with overridden=false is ignored by the renderer no matter what it reads."));
468
+ return MCPResult(Result);
469
+ }
470
+
471
+
472
+ // level(set_fixed_exposure): the common case of #950, done correctly in one
473
+ // call. Eye adaptation is disabled when the min and max adaptation brightnesses
474
+ // are equal, and both are ignored until their override bits are on, so the
475
+ // two-value-plus-two-bit write is what "fixed exposure" actually means.
476
+ TSharedPtr<FJsonValue> FLevelHandlers::SetFixedExposure(const TSharedPtr<FJsonObject>& Params)
477
+ {
478
+ REQUIRE_EDITOR_WORLD(World);
479
+
480
+ AActor* Actor = nullptr;
481
+ FMCPPostProcessTarget Target;
482
+ TSharedPtr<FJsonValue> ResolveError;
483
+ if (!ResolveActorAndPostProcessTarget(World, Params, Actor, Target, ResolveError)) return ResolveError;
484
+
485
+ if (!Params->HasField(TEXT("exposure")) && !Params->HasField(TEXT("brightness")))
486
+ {
487
+ return MCPError(TEXT("Missing 'exposure' (the fixed adaptation brightness written to both AutoExposureMinBrightness and AutoExposureMaxBrightness)"));
488
+ }
489
+ const double Exposure = Params->HasField(TEXT("exposure"))
490
+ ? OptionalNumber(Params, TEXT("exposure"), 1.0)
491
+ : OptionalNumber(Params, TEXT("brightness"), 1.0);
492
+
493
+ TArray<TPair<FString, TSharedPtr<FJsonValue>>> Writes;
494
+ Writes.Add(TPair<FString, TSharedPtr<FJsonValue>>(
495
+ TEXT("AutoExposureMinBrightness"), MakeShared<FJsonValueNumber>(Exposure)));
496
+ Writes.Add(TPair<FString, TSharedPtr<FJsonValue>>(
497
+ TEXT("AutoExposureMaxBrightness"), MakeShared<FJsonValueNumber>(Exposure)));
498
+ if (Params->HasField(TEXT("bias")))
499
+ {
500
+ Writes.Add(TPair<FString, TSharedPtr<FJsonValue>>(
501
+ TEXT("AutoExposureBias"), MakeShared<FJsonValueNumber>(OptionalNumber(Params, TEXT("bias"), 0.0))));
502
+ }
503
+
504
+ Target.Owner->Modify();
505
+ TArray<TSharedPtr<FJsonValue>> Applied;
506
+ int32 FailureCount = 0;
507
+ for (const TPair<FString, TSharedPtr<FJsonValue>>& Write : Writes)
508
+ {
509
+ bool bOk = false;
510
+ Applied.Add(MakeShared<FJsonValueObject>(
511
+ ApplyPostProcessSetting(Target, Write.Key, Write.Value, /*bEnableOverride*/ true, bOk)));
512
+ if (!bOk) ++FailureCount;
513
+ }
514
+ Target.Owner->PostEditChange();
515
+ Actor->MarkPackageDirty();
516
+
517
+ auto Result = MCPSuccess();
518
+ MCPSetUpdated(Result);
519
+ EmitPostProcessTargetFields(Result, Actor, Target);
520
+ Result->SetNumberField(TEXT("exposure"), Exposure);
521
+ Result->SetNumberField(TEXT("failedCount"), FailureCount);
522
+ Result->SetArrayField(TEXT("settings"), Applied);
523
+ if (FailureCount > 0) Result->SetBoolField(TEXT("success"), false);
524
+ Result->SetStringField(TEXT("note"),
525
+ TEXT("Min and max adaptation brightness are set to the same value, which is how the engine disables eye adaptation, and both override flags are enabled so the values apply. The level is left dirty and is NOT saved."));
526
+ return MCPResult(Result);
527
+ }