ue-mcp 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +4 -3
  2. package/dist/action-class.js +56 -3
  3. package/dist/action-class.js.map +1 -1
  4. package/dist/bridge-timeouts.d.ts +64 -0
  5. package/dist/bridge-timeouts.js +118 -0
  6. package/dist/bridge-timeouts.js.map +1 -0
  7. package/dist/bridge.js +10 -2
  8. package/dist/bridge.js.map +1 -1
  9. package/dist/editor-control.d.ts +72 -5
  10. package/dist/editor-control.js +164 -140
  11. package/dist/editor-control.js.map +1 -1
  12. package/dist/editor-target.d.ts +55 -1
  13. package/dist/editor-target.js +107 -10
  14. package/dist/editor-target.js.map +1 -1
  15. package/dist/engine-observer.d.ts +59 -5
  16. package/dist/engine-observer.js +249 -29
  17. package/dist/engine-observer.js.map +1 -1
  18. package/dist/engine-root.d.ts +91 -0
  19. package/dist/engine-root.js +448 -0
  20. package/dist/engine-root.js.map +1 -0
  21. package/dist/epic-enrich.js +16 -1
  22. package/dist/epic-enrich.js.map +1 -1
  23. package/dist/feedback-cli.js +4 -0
  24. package/dist/feedback-cli.js.map +1 -1
  25. package/dist/feedback-deferred.d.ts +9 -0
  26. package/dist/feedback-deferred.js +2 -0
  27. package/dist/feedback-deferred.js.map +1 -1
  28. package/dist/feedback-fallback.d.ts +99 -0
  29. package/dist/feedback-fallback.js +173 -0
  30. package/dist/feedback-fallback.js.map +1 -0
  31. package/dist/flow/write-methods.js +20 -0
  32. package/dist/flow/write-methods.js.map +1 -1
  33. package/dist/index.js +41 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/lean-context.js +8 -3
  36. package/dist/lean-context.js.map +1 -1
  37. package/dist/locking.js +11 -2
  38. package/dist/locking.js.map +1 -1
  39. package/dist/plugin/loader.d.ts +4 -0
  40. package/dist/plugin/loader.js +22 -8
  41. package/dist/plugin/loader.js.map +1 -1
  42. package/dist/plugin/manifest.d.ts +72 -52
  43. package/dist/plugin/manifest.js +118 -21
  44. package/dist/plugin/manifest.js.map +1 -1
  45. package/dist/plugin-cli.js +16 -3
  46. package/dist/plugin-cli.js.map +1 -1
  47. package/dist/project.d.ts +11 -0
  48. package/dist/project.js +23 -2
  49. package/dist/project.js.map +1 -1
  50. package/dist/python-gate.d.ts +95 -0
  51. package/dist/python-gate.js +175 -0
  52. package/dist/python-gate.js.map +1 -0
  53. package/dist/tool-counts.json +16 -16
  54. package/dist/tools/animation.js +288 -24
  55. package/dist/tools/animation.js.map +1 -1
  56. package/dist/tools/asset.js +79 -14
  57. package/dist/tools/asset.js.map +1 -1
  58. package/dist/tools/audio.js +59 -5
  59. package/dist/tools/audio.js.map +1 -1
  60. package/dist/tools/blueprint.js +29 -12
  61. package/dist/tools/blueprint.js.map +1 -1
  62. package/dist/tools/chooser.js +4 -1
  63. package/dist/tools/chooser.js.map +1 -1
  64. package/dist/tools/editor.js +81 -43
  65. package/dist/tools/editor.js.map +1 -1
  66. package/dist/tools/feedback.js +214 -48
  67. package/dist/tools/feedback.js.map +1 -1
  68. package/dist/tools/foliage.js +27 -1
  69. package/dist/tools/foliage.js.map +1 -1
  70. package/dist/tools/gameplay.js +38 -11
  71. package/dist/tools/gameplay.js.map +1 -1
  72. package/dist/tools/gas.js +17 -5
  73. package/dist/tools/gas.js.map +1 -1
  74. package/dist/tools/landscape.js +22 -7
  75. package/dist/tools/landscape.js.map +1 -1
  76. package/dist/tools/level.js +322 -51
  77. package/dist/tools/level.js.map +1 -1
  78. package/dist/tools/material.js +10 -4
  79. package/dist/tools/material.js.map +1 -1
  80. package/dist/tools/niagara.js +3 -2
  81. package/dist/tools/niagara.js.map +1 -1
  82. package/dist/tools/pcg.js +6 -5
  83. package/dist/tools/pcg.js.map +1 -1
  84. package/dist/tools/plugins.js +2 -0
  85. package/dist/tools/plugins.js.map +1 -1
  86. package/dist/tools/project.js +47 -20
  87. package/dist/tools/project.js.map +1 -1
  88. package/dist/types.d.ts +24 -0
  89. package/dist/types.js +46 -3
  90. package/dist/types.js.map +1 -1
  91. package/dist/ue-mcp.default.yml +493 -125
  92. package/package.json +2 -2
  93. package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
  94. package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
  95. package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
  141. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
  142. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
  143. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
  144. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
  145. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
  146. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
  147. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
  148. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
  149. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
  150. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
  151. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
  152. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
  153. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
  154. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
  155. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
  156. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
  157. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
  158. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
  159. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
  160. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
  161. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
  162. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
  163. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
  164. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
  165. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
  166. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
  167. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
  168. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
  169. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
  170. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
  171. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
  172. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
  173. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
  174. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
  175. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
  176. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
  177. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
  178. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
  179. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
  180. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
  181. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
  182. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
  183. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
  184. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
  185. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
  186. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
  187. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
  188. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
  189. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
  190. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
  191. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
  192. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
  193. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
  194. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
  195. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
  196. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
  197. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
  198. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
  199. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
  200. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
  201. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
  202. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
  203. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
  204. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
  205. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
  206. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
  207. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
  208. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
  209. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
  210. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
  211. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
  212. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
  213. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
  214. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
  215. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
  216. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
  217. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
  218. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
  219. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
  220. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
  221. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
  222. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
  223. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
  224. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
  225. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
  226. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
  227. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
  228. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
  229. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
  230. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
  231. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
  232. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
  233. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
  234. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
  235. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
  236. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
  237. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
  238. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
  239. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
  240. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
  241. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
  242. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
  243. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
  244. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
  245. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
  246. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
  247. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
  248. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
  249. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  250. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
  251. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
  252. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
  253. package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
  254. package/skills/ue-mcp-animation/SKILL.md +154 -0
  255. package/skills/ue-mcp-blueprint/SKILL.md +71 -0
@@ -0,0 +1,170 @@
1
+ // Coverage for #916.
2
+ //
3
+ // asset(mesh_boolean) writes a StaticMesh, and everything it can refuse it
4
+ // refuses BEFORE it touches one. These assertions cover that front half: the
5
+ // argument validation, the destructive-form guard, and the protected-mount
6
+ // guardrail, all of which run before the Geometry Script plugin is even asked
7
+ // for. That is deliberate, and it is what makes them assertable on a machine
8
+ // where the plugin is disabled.
9
+ //
10
+ // The boolean itself needs two real meshes and the Geometry Script plugin, so
11
+ // it belongs to the live smoke pass rather than here. Nothing in this file
12
+ // creates, loads or writes an asset.
13
+
14
+ #if WITH_DEV_AUTOMATION_TESTS
15
+
16
+ #include "HandlerRegistry.h"
17
+ #include "HandlerUtils.h"
18
+ #include "Handlers/AssetHandlers_MeshBoolean.h"
19
+
20
+ #include "Dom/JsonObject.h"
21
+ #include "Dom/JsonValue.h"
22
+ #include "Misc/AutomationTest.h"
23
+
24
+ namespace
25
+ {
26
+ TSharedPtr<FJsonObject> BooleanResponseObject(const TSharedPtr<FJsonValue>& Response)
27
+ {
28
+ return (Response.IsValid() && Response->Type == EJson::Object)
29
+ ? Response->AsObject()
30
+ : TSharedPtr<FJsonObject>();
31
+ }
32
+
33
+ bool BooleanResponseBool(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field, bool bDefault = false)
34
+ {
35
+ const TSharedPtr<FJsonObject> Obj = BooleanResponseObject(Response);
36
+ if (!Obj.IsValid()) return bDefault;
37
+ bool bValue = bDefault;
38
+ Obj->TryGetBoolField(Field, bValue);
39
+ return bValue;
40
+ }
41
+
42
+ FString BooleanResponseString(const TSharedPtr<FJsonValue>& Response, const TCHAR* Field)
43
+ {
44
+ const TSharedPtr<FJsonObject> Obj = BooleanResponseObject(Response);
45
+ if (!Obj.IsValid()) return FString();
46
+ FString Value;
47
+ Obj->TryGetStringField(Field, Value);
48
+ return Value;
49
+ }
50
+
51
+ /** The shape every case here sends, with one field swapped per case. */
52
+ TSharedPtr<FJsonObject> BaseBooleanParams()
53
+ {
54
+ TSharedPtr<FJsonObject> Params = MakeShared<FJsonObject>();
55
+ Params->SetStringField(TEXT("operation"), TEXT("subtract"));
56
+ Params->SetStringField(TEXT("targetPath"), TEXT("/Game/UEMCPNeverExists/SM_Target"));
57
+ Params->SetStringField(TEXT("toolPath"), TEXT("/Game/UEMCPNeverExists/SM_Tool"));
58
+ return Params;
59
+ }
60
+ }
61
+
62
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
63
+ FMCPMeshBooleanRefusesBeforeWritingTest,
64
+ "UE.MCP.Asset.MeshBoolean.RefusesBeforeItTouchesAnAsset",
65
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
66
+
67
+ bool FMCPMeshBooleanRefusesBeforeWritingTest::RunTest(const FString& Parameters)
68
+ {
69
+ FMCPHandlerRegistry Registry;
70
+ FAssetMeshBooleanHandlers::RegisterHandlers(Registry);
71
+ if (!TestTrue(TEXT("mesh_boolean is registered"), Registry.HasHandler(TEXT("mesh_boolean")))) return false;
72
+
73
+ // The boolean can run for minutes on a repair-sized mesh, so it registers a
74
+ // timeout of its own. The client mirrors that number by hand, and this is
75
+ // the assertion that the registration it mirrors actually happened.
76
+ TestEqual(TEXT("mesh_boolean registers its own game-thread timeout"),
77
+ Registry.GetHandlerTimeout(TEXT("mesh_boolean")), 300.0f);
78
+
79
+ {
80
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
81
+ Params->SetStringField(TEXT("operation"), TEXT("smoosh"));
82
+
83
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
84
+ TestFalse(TEXT("an unknown operation is refused"),
85
+ BooleanResponseBool(Response, TEXT("success"), true));
86
+ TestTrue(TEXT("an unknown operation lists the ones that exist"),
87
+ BooleanResponseString(Response, TEXT("error")).Contains(TEXT("subtract")));
88
+ }
89
+
90
+ {
91
+ // Two destinations, no way to honour both. Guessing here would pick
92
+ // between writing a new asset and overwriting the caller's target.
93
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
94
+ Params->SetBoolField(TEXT("inPlace"), true);
95
+ Params->SetStringField(TEXT("outputPath"), TEXT("/Game/UEMCPNeverExists/SM_Result"));
96
+
97
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
98
+ TestFalse(TEXT("outputPath together with inPlace is refused"),
99
+ BooleanResponseBool(Response, TEXT("success"), true));
100
+ TestTrue(TEXT("the refusal says both were given"),
101
+ BooleanResponseString(Response, TEXT("error")).Contains(TEXT("inPlace")));
102
+ }
103
+
104
+ {
105
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
106
+ Params->SetStringField(TEXT("lodType"), TEXT("WhicheverIsFine"));
107
+
108
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
109
+ TestFalse(TEXT("an unknown lodType is refused"),
110
+ BooleanResponseBool(Response, TEXT("success"), true));
111
+ TestTrue(TEXT("the refusal lists the LOD selectors"),
112
+ BooleanResponseString(Response, TEXT("error")).Contains(TEXT("MaxAvailable")));
113
+ }
114
+
115
+ {
116
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
117
+ Params->SetStringField(TEXT("nanite"), TEXT("maybe"));
118
+
119
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
120
+ TestFalse(TEXT("an unknown nanite mode is refused"),
121
+ BooleanResponseBool(Response, TEXT("success"), true));
122
+ TestTrue(TEXT("the refusal lists the nanite modes"),
123
+ BooleanResponseString(Response, TEXT("error")).Contains(TEXT("inherit")));
124
+ }
125
+
126
+ {
127
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
128
+ Params->SetStringField(TEXT("onConflict"), TEXT("clobber"));
129
+
130
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
131
+ TestFalse(TEXT("an unknown onConflict is refused"),
132
+ BooleanResponseBool(Response, TEXT("success"), true));
133
+ }
134
+
135
+ {
136
+ // The guardrail runs before the Geometry Script plugin is asked for
137
+ // anything, so engine content is refused whether or not the plugin is
138
+ // installed on this machine.
139
+ TSharedPtr<FJsonObject> Params = BaseBooleanParams();
140
+ Params->SetStringField(TEXT("outputPath"), TEXT("/Engine/UEMCPNeverExists/SM_Result"));
141
+
142
+ const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(TEXT("mesh_boolean"), Params);
143
+ TestFalse(TEXT("an output path on a protected mount is refused"),
144
+ BooleanResponseBool(Response, TEXT("success"), true));
145
+ TestTrue(TEXT("the refusal names the guardrail"),
146
+ BooleanResponseString(Response, TEXT("error")).Contains(TEXT("protected mount")));
147
+ }
148
+
149
+ {
150
+ // Everything valid, nothing on disk. Whether the Geometry Script plugin
151
+ // is here or not, the answer is a named failure rather than a crash,
152
+ // and it is one of exactly two reasons.
153
+ const TSharedPtr<FJsonValue> Response =
154
+ Registry.ExecuteHandler(TEXT("mesh_boolean"), BaseBooleanParams());
155
+ TestFalse(TEXT("a missing target mesh is not a success"),
156
+ BooleanResponseBool(Response, TEXT("success"), true));
157
+
158
+ const FString Reason = BooleanResponseString(Response, TEXT("reason"));
159
+ TestTrue(FString::Printf(
160
+ TEXT("the failure is either the missing asset or the missing plugin, not something unnamed (got '%s')"),
161
+ *Reason),
162
+ Reason == TEXT("missing")
163
+ || Reason == TEXT("notIndexed")
164
+ || Reason == TEXT("geometry_scripting_unavailable"));
165
+ }
166
+
167
+ return true;
168
+ }
169
+
170
+ #endif
@@ -0,0 +1,116 @@
1
+ // Coverage for asset(get_mesh_geometry) and asset(measure_mesh_geometry) that
2
+ // is safe to run anywhere.
3
+ //
4
+ // run_automation_tests dispatches every EditorContext/EngineFilter test in the
5
+ // process when it is called without a filter, against whatever project the
6
+ // bridge is attached to. Both handlers here are reads, so nothing they do can
7
+ // mutate a project, but a test that named a real mesh would still only pass on
8
+ // the machine that had it. Everything below is registration and argument
9
+ // validation, which holds in any project; the geometry itself is exercised by
10
+ // the smoke suite against the dedicated test project.
11
+
12
+ #if WITH_DEV_AUTOMATION_TESTS
13
+
14
+ #include "HandlerRegistry.h"
15
+ #include "Handlers/AssetHandlers_Geometry.h"
16
+ #include "Misc/AutomationTest.h"
17
+
18
+ namespace
19
+ {
20
+ TSharedPtr<FJsonObject> MakeGeometryResponseObject(const TSharedPtr<FJsonValue>& Response)
21
+ {
22
+ return (Response.IsValid() && Response->Type == EJson::Object) ? Response->AsObject() : nullptr;
23
+ }
24
+ }
25
+
26
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
27
+ FAssetMeshGeometryRegistrationTest,
28
+ "UE.MCP.Asset.MeshGeometry.RegistrationAndValidation",
29
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
30
+
31
+ bool FAssetMeshGeometryRegistrationTest::RunTest(const FString& Parameters)
32
+ {
33
+ FMCPHandlerRegistry Registry;
34
+ FAssetGeometryHandlers::RegisterHandlers(Registry);
35
+ TestTrue(TEXT("get_mesh_geometry is registered"), Registry.HasHandler(TEXT("get_mesh_geometry")));
36
+ TestTrue(TEXT("measure_mesh_geometry is registered"), Registry.HasHandler(TEXT("measure_mesh_geometry")));
37
+
38
+ // Missing assetPath names the parameter it wants.
39
+ {
40
+ const TSharedPtr<FJsonObject> Missing = MakeGeometryResponseObject(
41
+ Registry.ExecuteHandler(TEXT("get_mesh_geometry"), MakeShared<FJsonObject>()));
42
+ TestTrue(TEXT("missing assetPath returns an object"), Missing.IsValid());
43
+ if (Missing.IsValid())
44
+ {
45
+ TestFalse(TEXT("missing assetPath is unsuccessful"), Missing->GetBoolField(TEXT("success")));
46
+ TestTrue(TEXT("missing assetPath names assetPath"),
47
+ Missing->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
48
+ }
49
+ }
50
+ {
51
+ const TSharedPtr<FJsonObject> Missing = MakeGeometryResponseObject(
52
+ Registry.ExecuteHandler(TEXT("measure_mesh_geometry"), MakeShared<FJsonObject>()));
53
+ TestTrue(TEXT("measure missing assetPath returns an object"), Missing.IsValid());
54
+ if (Missing.IsValid())
55
+ {
56
+ TestFalse(TEXT("measure missing assetPath is unsuccessful"), Missing->GetBoolField(TEXT("success")));
57
+ TestTrue(TEXT("measure missing assetPath names assetPath"),
58
+ Missing->GetStringField(TEXT("error")).Contains(TEXT("assetPath")));
59
+ }
60
+ }
61
+
62
+ // An unknown include token is rejected before anything is loaded, and the
63
+ // message carries the full allowed set so a caller does not have to guess.
64
+ {
65
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
66
+ Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
67
+ Request->SetArrayField(TEXT("include"), { MakeShared<FJsonValueString>(TEXT("tangents")) });
68
+ const TSharedPtr<FJsonObject> Rejected = MakeGeometryResponseObject(
69
+ Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
70
+ TestTrue(TEXT("bad include returns an object"), Rejected.IsValid());
71
+ if (Rejected.IsValid())
72
+ {
73
+ TestFalse(TEXT("bad include is unsuccessful"), Rejected->GetBoolField(TEXT("success")));
74
+ const FString Error = Rejected->GetStringField(TEXT("error"));
75
+ TestTrue(TEXT("bad include quotes the offending token"), Error.Contains(TEXT("tangents")));
76
+ TestTrue(TEXT("bad include lists the allowed set"), Error.Contains(TEXT("positions")));
77
+ TestTrue(TEXT("bad include lists triangles too"), Error.Contains(TEXT("triangles")));
78
+ }
79
+ }
80
+
81
+ // An empty include is a request for nothing, which is a mistake rather than
82
+ // an answer, so it is refused instead of returning empty section arrays.
83
+ {
84
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
85
+ Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
86
+ Request->SetArrayField(TEXT("include"), TArray<TSharedPtr<FJsonValue>>());
87
+ const TSharedPtr<FJsonObject> Rejected = MakeGeometryResponseObject(
88
+ Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
89
+ TestTrue(TEXT("empty include returns an object"), Rejected.IsValid());
90
+ if (Rejected.IsValid())
91
+ {
92
+ TestFalse(TEXT("empty include is unsuccessful"), Rejected->GetBoolField(TEXT("success")));
93
+ TestTrue(TEXT("empty include says what to pass"),
94
+ Rejected->GetStringField(TEXT("error")).Contains(TEXT("include")));
95
+ }
96
+ }
97
+
98
+ // A path that resolves to nothing reports the path, not a null dereference.
99
+ {
100
+ TSharedPtr<FJsonObject> Request = MakeShared<FJsonObject>();
101
+ Request->SetStringField(TEXT("assetPath"), TEXT("/Game/UEMCP/DoesNotExist_MeshGeometryTest"));
102
+ const TSharedPtr<FJsonObject> NotFound = MakeGeometryResponseObject(
103
+ Registry.ExecuteHandler(TEXT("get_mesh_geometry"), Request));
104
+ TestTrue(TEXT("unknown asset returns an object"), NotFound.IsValid());
105
+ if (NotFound.IsValid())
106
+ {
107
+ TestFalse(TEXT("unknown asset is unsuccessful"), NotFound->GetBoolField(TEXT("success")));
108
+ TestTrue(TEXT("unknown asset echoes the path"),
109
+ NotFound->GetStringField(TEXT("error")).Contains(TEXT("DoesNotExist_MeshGeometryTest")));
110
+ }
111
+ }
112
+
113
+ return true;
114
+ }
115
+
116
+ #endif // WITH_DEV_AUTOMATION_TESTS
@@ -0,0 +1,161 @@
1
+ // Regression coverage for #949.
2
+ //
3
+ // SaveAssetPackage built its target filename with the asset extension
4
+ // unconditionally, so every handler that mutated an actor or a component in the
5
+ // open level wrote Content/Maps/<Level>.uasset alongside the existing
6
+ // Content/Maps/<Level>.umap. Two package files then claimed the same long
7
+ // package name, level(save) kept writing one and the ~87 SaveAssetPackage call
8
+ // sites kept writing the other, and the two diverged in silence. Unreal resolves
9
+ // the ".uasset" first, so the fork is the copy that wins.
10
+ //
11
+ // The test writes nowhere near the attached project. run_automation_tests
12
+ // dispatches every EditorContext/EngineFilter test in the process against
13
+ // whatever project the bridge happens to be attached to, so this registers its
14
+ // own mount point over a fresh directory in the system temp area, does the whole
15
+ // exercise there, then unmounts and deletes it.
16
+
17
+ #if WITH_DEV_AUTOMATION_TESTS
18
+
19
+ #include "HandlerUtils.h"
20
+
21
+ #include "Engine/World.h"
22
+ #include "HAL/FileManager.h"
23
+ #include "HAL/PlatformProcess.h"
24
+ #include "Misc/AutomationTest.h"
25
+ #include "Misc/Guid.h"
26
+ #include "Misc/PackageName.h"
27
+ #include "Misc/Paths.h"
28
+ #include "UObject/Package.h"
29
+
30
+ namespace
31
+ {
32
+ const TCHAR* const MCPExtensionTestRoot = TEXT("/UEMCPPackageExtensionTest/");
33
+
34
+ /** Mount a private content root for the duration of the test and take it
35
+ * back down again, so no assertion here can touch a user's content. */
36
+ struct FScopedTestMount
37
+ {
38
+ FString RootPath;
39
+ FString ContentPath;
40
+
41
+ FScopedTestMount()
42
+ : RootPath(MCPExtensionTestRoot)
43
+ , ContentPath(FPaths::Combine(
44
+ FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserTempDir())),
45
+ FString(TEXT("UEMCPPackageExtensionTest")),
46
+ FGuid::NewGuid().ToString(EGuidFormats::Digits)))
47
+ {
48
+ IFileManager::Get().MakeDirectory(*ContentPath, /*Tree=*/true);
49
+ FPackageName::RegisterMountPoint(RootPath, ContentPath);
50
+ }
51
+
52
+ ~FScopedTestMount()
53
+ {
54
+ FPackageName::UnRegisterMountPoint(RootPath, ContentPath);
55
+ IFileManager::Get().DeleteDirectory(*ContentPath, /*RequireExists=*/false, /*Tree=*/true);
56
+ }
57
+
58
+ FScopedTestMount(const FScopedTestMount&) = delete;
59
+ FScopedTestMount& operator=(const FScopedTestMount&) = delete;
60
+ };
61
+ }
62
+
63
+ IMPLEMENT_SIMPLE_AUTOMATION_TEST(
64
+ FPackageSaveExtensionTest,
65
+ "UE.MCP.Save.PackageExtension.WorldPackagesWriteUMap",
66
+ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
67
+
68
+ bool FPackageSaveExtensionTest::RunTest(const FString& Parameters)
69
+ {
70
+ const FScopedTestMount Mount;
71
+ IFileManager& FM = IFileManager::Get();
72
+
73
+ // A package with no world in it keeps the asset extension. This is the half
74
+ // the old code got right, and it has to stay right: one-file-per-actor
75
+ // packages under __ExternalActors__ hold an AActor and no UWorld, and OFPA
76
+ // expects those on disk as ".uasset".
77
+ const FString AssetPackageName = FString(MCPExtensionTestRoot) + TEXT("AssetPackage");
78
+ UPackage* AssetPackage = CreatePackage(*AssetPackageName);
79
+ TestNotNull(TEXT("asset package created"), AssetPackage);
80
+ if (AssetPackage)
81
+ {
82
+ TestFalse(TEXT("a package with no world is not a map package"), IsMapPackage(AssetPackage));
83
+ TestEqual(TEXT("a package with no world takes the asset extension"),
84
+ PackageFileExtension(AssetPackage), FPackageName::GetAssetPackageExtension());
85
+ }
86
+
87
+ // The regression itself: an object whose outermost is a UWorld.
88
+ const FString WorldPackageName = FString(MCPExtensionTestRoot) + TEXT("TestLevel");
89
+ UPackage* WorldPackage = CreatePackage(*WorldPackageName);
90
+ TestNotNull(TEXT("world package created"), WorldPackage);
91
+ if (!WorldPackage)
92
+ {
93
+ return false;
94
+ }
95
+
96
+ UWorld* World = UWorld::CreateWorld(
97
+ EWorldType::Inactive,
98
+ /*bInformEngineOfWorld=*/false,
99
+ FName(TEXT("TestLevel")),
100
+ WorldPackage,
101
+ /*bAddToRoot=*/false);
102
+ TestNotNull(TEXT("world created inside the package"), World);
103
+ if (!World)
104
+ {
105
+ return false;
106
+ }
107
+ // Mirror what UWorldFactory stamps on a newly authored map, so the save has
108
+ // a top-level asset to pick up under SaveAssetPackage's TopLevelFlags.
109
+ World->SetFlags(RF_Public | RF_Standalone);
110
+ const FGCRootScope KeepWorldAlive(World);
111
+
112
+ TestTrue(TEXT("the world's outermost is the world package"), World->GetOutermost() == WorldPackage);
113
+ TestTrue(TEXT("a package holding a world is a map package"), IsMapPackage(WorldPackage));
114
+ TestEqual(TEXT("a world package takes the map extension"),
115
+ PackageFileExtension(WorldPackage), FPackageName::GetMapPackageExtension());
116
+
117
+ FString ResolvedFileName;
118
+ TestTrue(TEXT("the world package resolves to a filename"),
119
+ ResolvePackageFileName(WorldPackage, ResolvedFileName));
120
+ TestTrue(TEXT("the resolved filename is a .umap"),
121
+ ResolvedFileName.EndsWith(FPackageName::GetMapPackageExtension(), ESearchCase::IgnoreCase));
122
+ TestFalse(TEXT("the resolved filename is not a .uasset"),
123
+ ResolvedFileName.EndsWith(FPackageName::GetAssetPackageExtension(), ESearchCase::IgnoreCase));
124
+
125
+ FString BaseFileName;
126
+ TestTrue(TEXT("the package name maps into the test mount"),
127
+ FPackageName::TryConvertLongPackageNameToFilename(WorldPackageName, BaseFileName));
128
+ const FString MapFile = BaseFileName + FPackageName::GetMapPackageExtension();
129
+ const FString AssetSibling = BaseFileName + FPackageName::GetAssetPackageExtension();
130
+
131
+ TestFalse(TEXT("no map file on disk before the save"), FM.FileExists(*MapFile));
132
+ TestFalse(TEXT("no asset sibling on disk before the save"), FM.FileExists(*AssetSibling));
133
+
134
+ const bool bSaved = SaveAssetPackage(World);
135
+
136
+ // This is the assertion that fails on a #949 regression, and it holds
137
+ // whatever the save itself decided to do: the asset sibling must never be
138
+ // created for a package that already lives on disk as a map.
139
+ TestFalse(TEXT("saving a world package writes no .uasset sibling"), FM.FileExists(*AssetSibling));
140
+ if (bSaved)
141
+ {
142
+ TestTrue(TEXT("saving a world package writes the .umap"), FM.FileExists(*MapFile));
143
+ }
144
+ else
145
+ {
146
+ AddInfo(TEXT("SavePackage declined to write the test world; the .uasset sibling assertion still stands."));
147
+ }
148
+
149
+ // Leave nothing dirty behind: a later editor(save_dirty) would otherwise
150
+ // find these packages and try to flush them through an unmounted root.
151
+ WorldPackage->SetDirtyFlag(false);
152
+ if (AssetPackage)
153
+ {
154
+ AssetPackage->SetDirtyFlag(false);
155
+ }
156
+ World->DestroyWorld(/*bInformEngineOfWorld=*/false);
157
+
158
+ return true;
159
+ }
160
+
161
+ #endif