unreal-engine-mcp-server 0.4.7 → 0.5.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 (438) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter.yml +148 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +23 -0
  19. package/.github/workflows/labeler.yml +16 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +12 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +267 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -71
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -619
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  97. package/dist/tools/consolidated-tool-definitions.js +829 -496
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1026
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +3 -3
  161. package/dist/tools/logs.js +5 -57
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +183 -19
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -663
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -515
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1139
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +9 -57
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +243 -21
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -574
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -0,0 +1,917 @@
1
+ #include "McpAutomationBridge_SCSHandlers.h"
2
+ #include "Async/Async.h"
3
+ #include "McpAutomationBridgeHelpers.h"
4
+ #include "McpAutomationBridgeSubsystem.h"
5
+ #include "Misc/ScopeExit.h"
6
+
7
+ #if WITH_EDITOR
8
+ #include "Camera/CameraComponent.h"
9
+ #include "Components/ActorComponent.h"
10
+ #include "Components/PointLightComponent.h"
11
+ #include "Components/SceneComponent.h"
12
+ #include "Components/SkeletalMeshComponent.h"
13
+ #include "Components/SpotLightComponent.h"
14
+ #include "Components/StaticMeshComponent.h"
15
+ #include "EditorAssetLibrary.h"
16
+ #include "Engine/Blueprint.h"
17
+ #include "Engine/SCS_Node.h"
18
+ #include "Engine/SimpleConstructionScript.h"
19
+ #include "Engine/SkeletalMesh.h"
20
+ #include "Engine/StaticMesh.h"
21
+ #include "Engine/StaticMeshActor.h"
22
+ #include "Kismet2/BlueprintEditorUtils.h"
23
+ #include "Kismet2/KismetEditorUtilities.h"
24
+ #include "Materials/MaterialInterface.h"
25
+ #include "UObject/UObjectIterator.h"
26
+
27
+ #endif
28
+
29
+ #if !WITH_EDITOR
30
+ static TSharedPtr<FJsonObject> UnsupportedSCSAction() {
31
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
32
+ Result->SetBoolField(TEXT("success"), false);
33
+ Result->SetStringField(TEXT("error"),
34
+ TEXT("SCS operations require editor build"));
35
+ return Result;
36
+ }
37
+ #endif
38
+
39
+ #if WITH_EDITOR
40
+ void FSCSHandlers::FinalizeBlueprintSCSChange(UBlueprint *Blueprint,
41
+ bool &bOutCompiled,
42
+ bool &bOutSaved) {
43
+ bOutCompiled = false;
44
+ bOutSaved = false;
45
+
46
+ if (!Blueprint) {
47
+ return;
48
+ }
49
+
50
+ FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
51
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
52
+ bOutCompiled = true;
53
+ bOutSaved = SaveLoadedAssetThrottled(Blueprint);
54
+ if (!bOutSaved) {
55
+ UE_LOG(LogMcpAutomationBridgeSubsystem, Warning,
56
+ TEXT("SaveLoadedAssetThrottled reported failure for '%s' after SCS "
57
+ "change"),
58
+ *Blueprint->GetPathName());
59
+ }
60
+ }
61
+
62
+ // Check if Play In Editor (PIE) is currently active
63
+ static bool IsPlayInEditorActive() {
64
+ if (GEditor && GEditor->IsPlaySessionInProgress()) {
65
+ return true;
66
+ }
67
+ // Additional check for standalone game instances
68
+ if (GEditor) {
69
+ for (const FWorldContext &Context : GEngine->GetWorldContexts()) {
70
+ if (Context.WorldType == EWorldType::PIE ||
71
+ Context.WorldType == EWorldType::Game) {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+
79
+ // Return PIE error result for SCS operations
80
+ static TSharedPtr<FJsonObject> PIEActiveError() {
81
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
82
+ Result->SetBoolField(TEXT("success"), false);
83
+ Result->SetStringField(
84
+ TEXT("error"),
85
+ TEXT("SCS operations cannot modify Blueprints during Play In Editor "
86
+ "(PIE). Please stop the play session first."));
87
+ Result->SetStringField(TEXT("errorCode"), TEXT("PIE_ACTIVE"));
88
+ return Result;
89
+ }
90
+ #endif
91
+
92
+ // Get Blueprint SCS structure
93
+ TSharedPtr<FJsonObject>
94
+ FSCSHandlers::GetBlueprintSCS(const FString &BlueprintPath) {
95
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
96
+
97
+ #if WITH_EDITOR
98
+ // Load blueprint
99
+ // Load blueprint
100
+ FString NormalizedPath;
101
+ FString ErrorMsg;
102
+ UBlueprint *Blueprint =
103
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
104
+ if (!Blueprint) {
105
+ Result->SetBoolField(TEXT("success"), false);
106
+ Result->SetStringField(
107
+ TEXT("error"),
108
+ ErrorMsg.IsEmpty()
109
+ ? FString::Printf(
110
+ TEXT(
111
+ "Blueprint not found or not a valid Blueprint asset: %s"),
112
+ *BlueprintPath)
113
+ : ErrorMsg);
114
+ return Result;
115
+ }
116
+
117
+ // Get SCS
118
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
119
+ if (!SCS) {
120
+ Result->SetBoolField(TEXT("success"), false);
121
+ Result->SetStringField(TEXT("error"),
122
+ TEXT("Blueprint has no SimpleConstructionScript"));
123
+ return Result;
124
+ }
125
+
126
+ // Build component tree
127
+ TArray<TSharedPtr<FJsonValue>> Components;
128
+ const TArray<USCS_Node *> &AllNodes = SCS->GetAllNodes();
129
+
130
+ for (USCS_Node *Node : AllNodes) {
131
+ if (Node && Node->GetVariableName().IsValid()) {
132
+ TSharedPtr<FJsonObject> ComponentObj = MakeShareable(new FJsonObject);
133
+ ComponentObj->SetStringField(TEXT("name"),
134
+ Node->GetVariableName().ToString());
135
+ ComponentObj->SetStringField(
136
+ TEXT("class"), Node->ComponentClass ? Node->ComponentClass->GetName()
137
+ : TEXT("Unknown"));
138
+ if (!Node->ParentComponentOrVariableName.IsNone()) {
139
+ ComponentObj->SetStringField(
140
+ TEXT("parent"), Node->ParentComponentOrVariableName.ToString());
141
+ }
142
+
143
+ // Add transform if component template exists (cast to SceneComponent for
144
+ // UE 5.6)
145
+ if (Node->ComponentTemplate) {
146
+ FTransform Transform = FTransform::Identity;
147
+ if (USceneComponent *SceneComp =
148
+ Cast<USceneComponent>(Node->ComponentTemplate)) {
149
+ Transform = SceneComp->GetRelativeTransform();
150
+ }
151
+ TSharedPtr<FJsonObject> TransformObj = MakeShareable(new FJsonObject);
152
+
153
+ FVector Loc = Transform.GetLocation();
154
+ FRotator Rot = Transform.GetRotation().Rotator();
155
+ FVector Scale = Transform.GetScale3D();
156
+
157
+ TransformObj->SetStringField(
158
+ TEXT("location"),
159
+ FString::Printf(TEXT("X=%.2f Y=%.2f Z=%.2f"), Loc.X, Loc.Y, Loc.Z));
160
+ TransformObj->SetStringField(
161
+ TEXT("rotation"), FString::Printf(TEXT("P=%.2f Y=%.2f R=%.2f"),
162
+ Rot.Pitch, Rot.Yaw, Rot.Roll));
163
+ TransformObj->SetStringField(
164
+ TEXT("scale"), FString::Printf(TEXT("X=%.2f Y=%.2f Z=%.2f"),
165
+ Scale.X, Scale.Y, Scale.Z));
166
+
167
+ ComponentObj->SetObjectField(TEXT("transform"), TransformObj);
168
+ }
169
+
170
+ // Add child count
171
+ ComponentObj->SetNumberField(TEXT("child_count"),
172
+ Node->GetChildNodes().Num());
173
+
174
+ Components.Add(MakeShareable(new FJsonValueObject(ComponentObj)));
175
+ }
176
+ }
177
+
178
+ Result->SetBoolField(TEXT("success"), true);
179
+ Result->SetArrayField(TEXT("components"), Components);
180
+ Result->SetNumberField(TEXT("count"), Components.Num());
181
+ Result->SetStringField(TEXT("blueprint_path"), BlueprintPath);
182
+ #else
183
+ Result->SetBoolField(TEXT("success"), false);
184
+ Result->SetStringField(TEXT("error"),
185
+ TEXT("SCS operations require editor build"));
186
+ return Result;
187
+ #endif
188
+
189
+ return Result;
190
+ }
191
+
192
+ // Add component to SCS
193
+ TSharedPtr<FJsonObject> FSCSHandlers::AddSCSComponent(
194
+ const FString &BlueprintPath, const FString &ComponentClass,
195
+ const FString &ComponentName, const FString &ParentComponentName,
196
+ const FString &MeshPath, const FString &MaterialPath) {
197
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
198
+
199
+ #if WITH_EDITOR
200
+ // Check for PIE - cannot modify Blueprints during play
201
+ if (IsPlayInEditorActive()) {
202
+ return PIEActiveError();
203
+ }
204
+
205
+ // Load blueprint
206
+ FString NormalizedPath;
207
+ FString ErrorMsg;
208
+ UBlueprint *Blueprint =
209
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
210
+ if (!Blueprint) {
211
+ Result->SetBoolField(TEXT("success"), false);
212
+ Result->SetStringField(
213
+ TEXT("error"),
214
+ ErrorMsg.IsEmpty()
215
+ ? FString::Printf(TEXT("Blueprint asset not found at path: %s"),
216
+ *BlueprintPath)
217
+ : ErrorMsg);
218
+ return Result;
219
+ }
220
+
221
+ // Get or create SCS
222
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
223
+ if (!SCS) {
224
+ SCS = NewObject<USimpleConstructionScript>(Blueprint);
225
+ Blueprint->SimpleConstructionScript = SCS;
226
+ }
227
+
228
+ // Find component class (UE 5.6: ANY_PACKAGE is deprecated, use nullptr)
229
+ // Use the robust utility to resolve class (handles NiagaraComponent, Assets,
230
+ // Native classes)
231
+ UClass *CompClass = ResolveClassByName(ComponentClass);
232
+
233
+ if (!CompClass) {
234
+ Result->SetBoolField(TEXT("success"), false);
235
+ Result->SetStringField(
236
+ TEXT("error"), FString::Printf(TEXT("Component class not found: %s"),
237
+ *ComponentClass));
238
+ return Result;
239
+ }
240
+
241
+ // Verify it's a component class
242
+ if (!CompClass->IsChildOf(UActorComponent::StaticClass())) {
243
+ Result->SetBoolField(TEXT("success"), false);
244
+ Result->SetStringField(
245
+ TEXT("error"),
246
+ FString::Printf(TEXT("Class is not a component: %s"), *ComponentClass));
247
+ return Result;
248
+ }
249
+
250
+ // Find parent node if specified
251
+ USCS_Node *ParentNode = nullptr;
252
+ if (!ParentComponentName.IsEmpty()) {
253
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
254
+ if (Node && Node->GetVariableName().IsValid() &&
255
+ Node->GetVariableName().ToString().Equals(ParentComponentName,
256
+ ESearchCase::IgnoreCase)) {
257
+ ParentNode = Node;
258
+ break;
259
+ }
260
+ }
261
+
262
+ if (!ParentNode) {
263
+ Result->SetBoolField(TEXT("success"), false);
264
+ Result->SetStringField(
265
+ TEXT("error"), FString::Printf(TEXT("Parent component not found: %s"),
266
+ *ParentComponentName));
267
+ return Result;
268
+ }
269
+ }
270
+
271
+ // Check for duplicate name
272
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
273
+ if (Node && Node->GetVariableName().IsValid() &&
274
+ Node->GetVariableName().ToString().Equals(ComponentName,
275
+ ESearchCase::IgnoreCase)) {
276
+ Result->SetBoolField(TEXT("success"), false);
277
+ Result->SetStringField(
278
+ TEXT("error"),
279
+ FString::Printf(TEXT("Component with name '%s' already exists"),
280
+ *ComponentName));
281
+ return Result;
282
+ }
283
+ }
284
+
285
+ // Create new node
286
+ USCS_Node *NewNode = SCS->CreateNode(CompClass, FName(*ComponentName));
287
+ if (!NewNode) {
288
+ Result->SetBoolField(TEXT("success"), false);
289
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create SCS node"));
290
+ return Result;
291
+ }
292
+
293
+ // Explicitly set the variable name to ensure it's properly registered
294
+ NewNode->SetVariableName(FName(*ComponentName));
295
+
296
+ // Set parent or add as root
297
+ if (ParentNode) {
298
+ ParentNode->AddChildNode(NewNode);
299
+ } else {
300
+ SCS->AddNode(NewNode);
301
+ }
302
+
303
+ // Apply mesh if specified (Feature #1: Mesh Assignment)
304
+ // Uses: SetStaticMesh - Engine/StaticMeshComponent.cpp:2265
305
+ // SetSkeletalMesh - Engine/SkeletalMeshComponent.cpp:3322
306
+ bool bMeshApplied = false;
307
+ if (!MeshPath.IsEmpty() && NewNode->ComponentTemplate) {
308
+ if (UStaticMeshComponent *SMC =
309
+ Cast<UStaticMeshComponent>(NewNode->ComponentTemplate)) {
310
+ UStaticMesh *Mesh = LoadObject<UStaticMesh>(nullptr, *MeshPath);
311
+ if (Mesh) {
312
+ SMC->SetStaticMesh(Mesh);
313
+ bMeshApplied = true;
314
+ }
315
+ } else if (USkeletalMeshComponent *SkMC =
316
+ Cast<USkeletalMeshComponent>(NewNode->ComponentTemplate)) {
317
+ USkeletalMesh *Mesh = LoadObject<USkeletalMesh>(nullptr, *MeshPath);
318
+ if (Mesh) {
319
+ SkMC->SetSkeletalMesh(Mesh, true);
320
+ bMeshApplied = true;
321
+ }
322
+ }
323
+ }
324
+
325
+ // Apply material if specified (Feature #2: Material Assignment)
326
+ // Uses: SetMaterial - Engine/PrimitiveComponent.cpp:2477
327
+ bool bMaterialApplied = false;
328
+ if (!MaterialPath.IsEmpty() && NewNode->ComponentTemplate) {
329
+ if (UPrimitiveComponent *PC =
330
+ Cast<UPrimitiveComponent>(NewNode->ComponentTemplate)) {
331
+ UMaterialInterface *Mat =
332
+ LoadObject<UMaterialInterface>(nullptr, *MaterialPath);
333
+ if (Mat) {
334
+ PC->SetMaterial(0, Mat);
335
+ bMaterialApplied = true;
336
+ }
337
+ }
338
+ }
339
+
340
+ // Finalize blueprint change (compile/save)
341
+ bool bCompiled = false;
342
+ bool bSaved = false;
343
+ FinalizeBlueprintSCSChange(Blueprint, bCompiled, bSaved);
344
+
345
+ // Real test: Verify component exists in SCS
346
+ bool bVerified = false;
347
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
348
+ if (Node && Node->GetVariableName().IsValid() &&
349
+ Node->GetVariableName().ToString().Equals(ComponentName,
350
+ ESearchCase::IgnoreCase)) {
351
+ bVerified = true;
352
+ break;
353
+ }
354
+ }
355
+
356
+ if (!bVerified) {
357
+ Result->SetBoolField(TEXT("success"), false);
358
+ Result->SetStringField(
359
+ TEXT("error"), FString::Printf(TEXT("Verification failed: Component "
360
+ "'%s' not found in SCS after add"),
361
+ *ComponentName));
362
+ return Result;
363
+ }
364
+
365
+ Result->SetBoolField(TEXT("success"), true);
366
+ Result->SetStringField(
367
+ TEXT("message"),
368
+ FString::Printf(TEXT("Component '%s' added to SCS"), *ComponentName));
369
+ Result->SetStringField(TEXT("component_name"), ComponentName);
370
+ Result->SetStringField(TEXT("component_class"), CompClass->GetName());
371
+ Result->SetStringField(TEXT("parent"), ParentComponentName.IsEmpty()
372
+ ? TEXT("(root)")
373
+ : ParentComponentName);
374
+ Result->SetBoolField(TEXT("compiled"), bCompiled);
375
+ Result->SetBoolField(TEXT("saved"), bSaved);
376
+ // Feature #1, #2: Report mesh/material assignment status
377
+ Result->SetBoolField(TEXT("mesh_applied"), bMeshApplied);
378
+ Result->SetBoolField(TEXT("material_applied"), bMaterialApplied);
379
+ #else
380
+ return UnsupportedSCSAction();
381
+ #endif
382
+
383
+ return Result;
384
+ }
385
+
386
+ // Remove component from SCS
387
+ TSharedPtr<FJsonObject>
388
+ FSCSHandlers::RemoveSCSComponent(const FString &BlueprintPath,
389
+ const FString &ComponentName) {
390
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
391
+
392
+ #if WITH_EDITOR
393
+ FString NormalizedPath;
394
+ FString ErrorMsg;
395
+ UBlueprint *Blueprint =
396
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
397
+ if (!Blueprint) {
398
+ Result->SetBoolField(TEXT("success"), false);
399
+ Result->SetStringField(
400
+ TEXT("error"),
401
+ ErrorMsg.IsEmpty()
402
+ ? FString::Printf(TEXT("Blueprint asset not found at path: %s"),
403
+ *BlueprintPath)
404
+ : ErrorMsg);
405
+ Result->SetStringField(TEXT("errorCode"), TEXT("ASSET_NOT_FOUND"));
406
+ return Result;
407
+ }
408
+
409
+ if (!Blueprint->SimpleConstructionScript) {
410
+ Result->SetBoolField(TEXT("success"), false);
411
+ Result->SetStringField(
412
+ TEXT("error"),
413
+ FString::Printf(TEXT("Blueprint has no SimpleConstructionScript: %s"),
414
+ *BlueprintPath));
415
+ Result->SetStringField(TEXT("errorCode"), TEXT("SCS_NOT_FOUND"));
416
+ return Result;
417
+ }
418
+
419
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
420
+
421
+ // Find node to remove
422
+ USCS_Node *NodeToRemove = nullptr;
423
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
424
+ if (Node && Node->GetVariableName().IsValid() &&
425
+ Node->GetVariableName().ToString().Equals(ComponentName,
426
+ ESearchCase::IgnoreCase)) {
427
+ NodeToRemove = Node;
428
+ break;
429
+ }
430
+ }
431
+
432
+ if (!NodeToRemove) {
433
+ Result->SetBoolField(TEXT("success"), false);
434
+ Result->SetStringField(
435
+ TEXT("error"),
436
+ FString::Printf(TEXT("Component not found: %s"), *ComponentName));
437
+ Result->SetStringField(TEXT("errorCode"), TEXT("SCS_COMPONENT_NOT_FOUND"));
438
+ return Result;
439
+ }
440
+
441
+ SCS->RemoveNode(NodeToRemove);
442
+
443
+ bool bCompiled = false;
444
+ bool bSaved = false;
445
+ FinalizeBlueprintSCSChange(Blueprint, bCompiled, bSaved);
446
+
447
+ Result->SetBoolField(TEXT("success"), true);
448
+ Result->SetStringField(
449
+ TEXT("message"),
450
+ FString::Printf(TEXT("Component '%s' removed from SCS"), *ComponentName));
451
+ Result->SetBoolField(TEXT("compiled"), bCompiled);
452
+ Result->SetBoolField(TEXT("saved"), bSaved);
453
+ #else
454
+ return UnsupportedSCSAction();
455
+ #endif
456
+
457
+ return Result;
458
+ }
459
+
460
+ // Reparent component within SCS
461
+ TSharedPtr<FJsonObject>
462
+ FSCSHandlers::ReparentSCSComponent(const FString &BlueprintPath,
463
+ const FString &ComponentName,
464
+ const FString &NewParentName) {
465
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
466
+
467
+ #if WITH_EDITOR
468
+ FString NormalizedPath;
469
+ FString ErrorMsg;
470
+ UBlueprint *Blueprint =
471
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
472
+ if (!Blueprint) {
473
+ Result->SetBoolField(TEXT("success"), false);
474
+ Result->SetStringField(
475
+ TEXT("error"),
476
+ ErrorMsg.IsEmpty()
477
+ ? FString::Printf(TEXT("Blueprint asset not found at path: %s"),
478
+ *BlueprintPath)
479
+ : ErrorMsg);
480
+ return Result;
481
+ }
482
+
483
+ if (!Blueprint->SimpleConstructionScript) {
484
+ Result->SetBoolField(TEXT("success"), false);
485
+ Result->SetStringField(
486
+ TEXT("error"),
487
+ FString::Printf(TEXT("Blueprint has no SimpleConstructionScript: %s"),
488
+ *BlueprintPath));
489
+ return Result;
490
+ }
491
+
492
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
493
+
494
+ // Find component to reparent
495
+ USCS_Node *ComponentNode = nullptr;
496
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
497
+ if (Node && Node->GetVariableName().IsValid() &&
498
+ Node->GetVariableName().ToString().Equals(ComponentName,
499
+ ESearchCase::IgnoreCase)) {
500
+ ComponentNode = Node;
501
+ break;
502
+ }
503
+ }
504
+
505
+ if (!ComponentNode) {
506
+ Result->SetBoolField(TEXT("success"), false);
507
+ Result->SetStringField(
508
+ TEXT("error"),
509
+ FString::Printf(TEXT("Component not found: %s"), *ComponentName));
510
+ return Result;
511
+ }
512
+
513
+ // Find new parent (empty string means root)
514
+ USCS_Node *NewParentNode = nullptr;
515
+ if (!NewParentName.IsEmpty()) {
516
+ // Accept common root synonyms
517
+ const bool bRootSynonym =
518
+ NewParentName.Equals(TEXT("RootComponent"), ESearchCase::IgnoreCase) ||
519
+ NewParentName.Equals(TEXT("DefaultSceneRoot"),
520
+ ESearchCase::IgnoreCase) ||
521
+ NewParentName.Equals(TEXT("Root"), ESearchCase::IgnoreCase);
522
+ if (bRootSynonym) {
523
+ const TArray<USCS_Node *> &Roots = SCS->GetRootNodes();
524
+ // Prefer an explicit DefaultSceneRoot if present
525
+ for (USCS_Node *R : Roots) {
526
+ if (R && R->GetVariableName().IsValid() &&
527
+ R->GetVariableName().ToString().Equals(TEXT("DefaultSceneRoot"),
528
+ ESearchCase::IgnoreCase)) {
529
+ NewParentNode = R;
530
+ break;
531
+ }
532
+ }
533
+ // Fallback: first root that is not the component itself
534
+ if (!NewParentNode) {
535
+ for (USCS_Node *R : Roots) {
536
+ if (R && R != ComponentNode) {
537
+ NewParentNode = R;
538
+ break;
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ if (!NewParentNode) {
545
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
546
+ if (Node && Node->GetVariableName().IsValid() &&
547
+ Node->GetVariableName().ToString().Equals(
548
+ NewParentName, ESearchCase::IgnoreCase)) {
549
+ NewParentNode = Node;
550
+ break;
551
+ }
552
+ }
553
+ }
554
+
555
+ if (!NewParentNode) {
556
+ // If caller asked for RootComponent and we can't resolve it, treat as a
557
+ // benign no-op
558
+ if (bRootSynonym) {
559
+ Result->SetBoolField(TEXT("success"), true);
560
+ Result->SetStringField(
561
+ TEXT("message"),
562
+ TEXT("Requested RootComponent not found; component remains at "
563
+ "current hierarchy (treated as success)."));
564
+ return Result;
565
+ }
566
+ Result->SetBoolField(TEXT("success"), false);
567
+ Result->SetStringField(
568
+ TEXT("error"),
569
+ FString::Printf(TEXT("New parent not found: %s"), *NewParentName));
570
+ return Result;
571
+ }
572
+ }
573
+
574
+ // Helper: check if B is a descendant of A (prevent cycles)
575
+ auto IsDescendantOf = [](USCS_Node *A, USCS_Node *B) -> bool {
576
+ if (!A || !B)
577
+ return false;
578
+ TArray<USCS_Node *> Stack;
579
+ Stack.Add(A);
580
+ while (Stack.Num() > 0) {
581
+ USCS_Node *Cur = Stack.Pop(EAllowShrinking::No);
582
+ if (!Cur)
583
+ continue;
584
+ const TArray<USCS_Node *> &Kids = Cur->GetChildNodes();
585
+ for (USCS_Node *K : Kids) {
586
+ if (!K)
587
+ continue;
588
+ if (K == B)
589
+ return true;
590
+ Stack.Add(K);
591
+ }
592
+ }
593
+ return false;
594
+ };
595
+
596
+ // Remove from current parent (UE 5.6: find parent manually)
597
+ USCS_Node *OldParent = nullptr;
598
+ for (USCS_Node *Candidate : SCS->GetAllNodes()) {
599
+ if (Candidate && Candidate->GetChildNodes().Contains(ComponentNode)) {
600
+ OldParent = Candidate;
601
+ break;
602
+ }
603
+ }
604
+
605
+ // No-op checks (already under desired parent)
606
+ if ((OldParent == nullptr && NewParentNode && SCS->GetRootNodes().Num() > 0 &&
607
+ NewParentNode == SCS->GetRootNodes()[0]) ||
608
+ (OldParent != nullptr && NewParentNode == OldParent)) {
609
+ Result->SetBoolField(TEXT("success"), true);
610
+ Result->SetStringField(
611
+ TEXT("message"),
612
+ TEXT("Component already under requested parent; no changes made"));
613
+ return Result;
614
+ }
615
+
616
+ // Prevent cycles: new parent cannot be a descendant of the component
617
+ if (NewParentNode && IsDescendantOf(ComponentNode, NewParentNode)) {
618
+ Result->SetBoolField(TEXT("success"), false);
619
+ Result->SetStringField(
620
+ TEXT("error"),
621
+ TEXT("Cannot create circular parent-child relationship"));
622
+ return Result;
623
+ }
624
+
625
+ // Detach from old parent
626
+ if (OldParent) {
627
+ OldParent->RemoveChildNode(ComponentNode);
628
+ } else {
629
+ // Was a root node; remove from root listing when reparenting to non-root
630
+ const bool bReparentingToRoot = (NewParentNode == nullptr);
631
+ if (!bReparentingToRoot) {
632
+ SCS->RemoveNode(ComponentNode);
633
+ }
634
+ // else already at root and staying root would have been returned above
635
+ }
636
+
637
+ // Attach to new parent or root
638
+ if (NewParentNode) {
639
+ NewParentNode->AddChildNode(ComponentNode);
640
+ } else {
641
+ SCS->AddNode(ComponentNode);
642
+ }
643
+
644
+ // Mark blueprint as modified and finalize change
645
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
646
+ bool bCompiled = false;
647
+ bool bSaved = false;
648
+ FinalizeBlueprintSCSChange(Blueprint, bCompiled, bSaved);
649
+
650
+ Result->SetBoolField(TEXT("success"), true);
651
+ Result->SetStringField(
652
+ TEXT("message"),
653
+ FString::Printf(TEXT("Component '%s' reparented to '%s'"), *ComponentName,
654
+ NewParentName.IsEmpty() ? TEXT("(root)")
655
+ : *NewParentName));
656
+ Result->SetBoolField(TEXT("compiled"), bCompiled);
657
+ Result->SetBoolField(TEXT("saved"), bSaved);
658
+ #else
659
+ return UnsupportedSCSAction();
660
+ #endif
661
+
662
+ return Result;
663
+ }
664
+
665
+ // Set component transform in SCS
666
+ TSharedPtr<FJsonObject> FSCSHandlers::SetSCSComponentTransform(
667
+ const FString &BlueprintPath, const FString &ComponentName,
668
+ const TSharedPtr<FJsonObject> &TransformData) {
669
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
670
+
671
+ #if WITH_EDITOR
672
+ FString NormalizedPath;
673
+ FString ErrorMsg;
674
+ UBlueprint *Blueprint =
675
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
676
+ if (!Blueprint) {
677
+ Result->SetBoolField(TEXT("success"), false);
678
+ Result->SetStringField(
679
+ TEXT("error"),
680
+ ErrorMsg.IsEmpty()
681
+ ? FString::Printf(TEXT("Blueprint asset not found at path: %s"),
682
+ *BlueprintPath)
683
+ : ErrorMsg);
684
+ Result->SetStringField(TEXT("errorCode"), TEXT("ASSET_NOT_FOUND"));
685
+ return Result;
686
+ }
687
+
688
+ if (!Blueprint->SimpleConstructionScript) {
689
+ Result->SetBoolField(TEXT("success"), false);
690
+ Result->SetStringField(
691
+ TEXT("error"),
692
+ FString::Printf(TEXT("Blueprint has no SimpleConstructionScript: %s"),
693
+ *BlueprintPath));
694
+ Result->SetStringField(TEXT("errorCode"), TEXT("SCS_NOT_FOUND"));
695
+ return Result;
696
+ }
697
+
698
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
699
+
700
+ // Find component
701
+ USCS_Node *ComponentNode = nullptr;
702
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
703
+ if (Node && Node->GetVariableName().IsValid() &&
704
+ Node->GetVariableName().ToString().Equals(ComponentName,
705
+ ESearchCase::IgnoreCase)) {
706
+ ComponentNode = Node;
707
+ break;
708
+ }
709
+ }
710
+
711
+ if (!ComponentNode || !ComponentNode->ComponentTemplate) {
712
+ Result->SetBoolField(TEXT("success"), false);
713
+ Result->SetStringField(
714
+ TEXT("error"),
715
+ FString::Printf(TEXT("Component or template not found: %s"),
716
+ *ComponentName));
717
+ Result->SetStringField(TEXT("errorCode"),
718
+ TEXT("SCS_COMPONENT_TEMPLATE_NOT_FOUND"));
719
+ return Result;
720
+ }
721
+
722
+ // Parse transform from JSON
723
+ FVector Location(0, 0, 0);
724
+ FRotator Rotation(0, 0, 0);
725
+ FVector Scale(1, 1, 1);
726
+
727
+ // Parse location array [x, y, z]
728
+ const TArray<TSharedPtr<FJsonValue>> *LocArray;
729
+ if (TransformData->TryGetArrayField(TEXT("location"), LocArray) &&
730
+ LocArray->Num() >= 3) {
731
+ Location.X = (*LocArray)[0]->AsNumber();
732
+ Location.Y = (*LocArray)[1]->AsNumber();
733
+ Location.Z = (*LocArray)[2]->AsNumber();
734
+ }
735
+
736
+ // Parse rotation array [pitch, yaw, roll]
737
+ const TArray<TSharedPtr<FJsonValue>> *RotArray;
738
+ if (TransformData->TryGetArrayField(TEXT("rotation"), RotArray) &&
739
+ RotArray->Num() >= 3) {
740
+ Rotation.Pitch = (*RotArray)[0]->AsNumber();
741
+ Rotation.Yaw = (*RotArray)[1]->AsNumber();
742
+ Rotation.Roll = (*RotArray)[2]->AsNumber();
743
+ }
744
+
745
+ // Parse scale array [x, y, z]
746
+ const TArray<TSharedPtr<FJsonValue>> *ScaleArray;
747
+ if (TransformData->TryGetArrayField(TEXT("scale"), ScaleArray) &&
748
+ ScaleArray->Num() >= 3) {
749
+ Scale.X = (*ScaleArray)[0]->AsNumber();
750
+ Scale.Y = (*ScaleArray)[1]->AsNumber();
751
+ Scale.Z = (*ScaleArray)[2]->AsNumber();
752
+ }
753
+
754
+ // Apply transform to component template
755
+ FTransform NewTransform(Rotation, Location, Scale);
756
+
757
+ if (USceneComponent *SceneComp =
758
+ Cast<USceneComponent>(ComponentNode->ComponentTemplate)) {
759
+ SceneComp->SetRelativeTransform(NewTransform);
760
+
761
+ bool bCompiled = false;
762
+ bool bSaved = false;
763
+ FinalizeBlueprintSCSChange(Blueprint, bCompiled, bSaved);
764
+
765
+ Result->SetBoolField(TEXT("success"), true);
766
+ Result->SetStringField(
767
+ TEXT("message"),
768
+ FString::Printf(TEXT("Transform set for component '%s'"),
769
+ *ComponentName));
770
+ Result->SetBoolField(TEXT("compiled"), bCompiled);
771
+ Result->SetBoolField(TEXT("saved"), bSaved);
772
+ } else {
773
+ Result->SetBoolField(TEXT("success"), false);
774
+ Result->SetStringField(
775
+ TEXT("error"),
776
+ TEXT("Component is not a SceneComponent (no transform)"));
777
+ Result->SetStringField(TEXT("errorCode"), TEXT("SCS_NOT_SCENE_COMPONENT"));
778
+ }
779
+ #else
780
+ return UnsupportedSCSAction();
781
+ #endif
782
+
783
+ return Result;
784
+ }
785
+
786
+ // Set component property in SCS
787
+ TSharedPtr<FJsonObject> FSCSHandlers::SetSCSComponentProperty(
788
+ const FString &BlueprintPath, const FString &ComponentName,
789
+ const FString &PropertyName, const TSharedPtr<FJsonValue> &PropertyValue) {
790
+ TSharedPtr<FJsonObject> Result = MakeShareable(new FJsonObject);
791
+
792
+ #if WITH_EDITOR
793
+ FString NormalizedPath;
794
+ FString ErrorMsg;
795
+ UBlueprint *Blueprint =
796
+ LoadBlueprintAsset(BlueprintPath, NormalizedPath, ErrorMsg);
797
+ if (!Blueprint) {
798
+ Result->SetBoolField(TEXT("success"), false);
799
+ Result->SetStringField(
800
+ TEXT("error"),
801
+ ErrorMsg.IsEmpty()
802
+ ? FString::Printf(TEXT("Blueprint asset not found at path: %s"),
803
+ *BlueprintPath)
804
+ : ErrorMsg);
805
+ Result->SetStringField(TEXT("errorCode"), TEXT("ASSET_NOT_FOUND"));
806
+ return Result;
807
+ }
808
+
809
+ if (!Blueprint->SimpleConstructionScript) {
810
+ Result->SetBoolField(TEXT("success"), false);
811
+ Result->SetStringField(
812
+ TEXT("error"),
813
+ FString::Printf(TEXT("Blueprint has no SimpleConstructionScript: %s"),
814
+ *BlueprintPath));
815
+ return Result;
816
+ }
817
+
818
+ USimpleConstructionScript *SCS = Blueprint->SimpleConstructionScript;
819
+
820
+ // Find component
821
+ USCS_Node *ComponentNode = nullptr;
822
+ for (USCS_Node *Node : SCS->GetAllNodes()) {
823
+ if (Node && Node->GetVariableName().IsValid() &&
824
+ Node->GetVariableName().ToString().Equals(ComponentName,
825
+ ESearchCase::IgnoreCase)) {
826
+ ComponentNode = Node;
827
+ break;
828
+ }
829
+ }
830
+
831
+ if (!ComponentNode || !ComponentNode->ComponentTemplate) {
832
+ Result->SetBoolField(TEXT("success"), false);
833
+ Result->SetStringField(
834
+ TEXT("error"),
835
+ FString::Printf(TEXT("Component or template not found: %s"),
836
+ *ComponentName));
837
+ Result->SetStringField(TEXT("errorCode"),
838
+ TEXT("SCS_COMPONENT_TEMPLATE_NOT_FOUND"));
839
+ return Result;
840
+ }
841
+
842
+ if (PropertyValue.IsValid()) {
843
+ // ResolveNestedPropertyPath now returns the container pointer
844
+ void *ContainerPtr = nullptr;
845
+ FString ResolveError;
846
+ FString FailureMessage;
847
+ FString FailureCode;
848
+ bool bAppliedValue = false;
849
+ FProperty *TargetProp =
850
+ ResolveNestedPropertyPath(ComponentNode->ComponentTemplate,
851
+ PropertyName, ContainerPtr, ResolveError);
852
+
853
+ if (!TargetProp || !ContainerPtr) {
854
+ Result->SetBoolField(TEXT("success"), false);
855
+ Result->SetStringField(
856
+ TEXT("error"),
857
+ ResolveError.IsEmpty()
858
+ ? FString::Printf(TEXT("Property not found: %s"), *PropertyName)
859
+ : ResolveError);
860
+ Result->SetStringField(TEXT("errorCode"), TEXT("SCS_PROPERTY_NOT_FOUND"));
861
+ return Result;
862
+ }
863
+
864
+ if (ApplyJsonValueToProperty(ContainerPtr, TargetProp, PropertyValue,
865
+ FailureMessage)) {
866
+ bAppliedValue = true;
867
+ } else {
868
+ FailureCode = TEXT("SCS_PROPERTY_APPLY_FAILED");
869
+ }
870
+
871
+ if (!bAppliedValue) {
872
+ Result->SetBoolField(TEXT("success"), false);
873
+ Result->SetStringField(TEXT("error"),
874
+ FailureMessage.IsEmpty()
875
+ ? TEXT("Failed to apply property value")
876
+ : FailureMessage);
877
+ if (!FailureCode.IsEmpty()) {
878
+ Result->SetStringField(TEXT("errorCode"), FailureCode);
879
+ }
880
+ return Result;
881
+ }
882
+ } else {
883
+ Result->SetBoolField(TEXT("success"), false);
884
+ Result->SetStringField(TEXT("error"), TEXT("Property value is invalid"));
885
+ Result->SetStringField(TEXT("errorCode"),
886
+ TEXT("SCS_PROPERTY_INVALID_VALUE"));
887
+ return Result;
888
+ }
889
+
890
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
891
+ bool bCompiled = false;
892
+ bool bSaved = false;
893
+ FinalizeBlueprintSCSChange(Blueprint, bCompiled, bSaved);
894
+
895
+ Result->SetBoolField(TEXT("success"), true);
896
+ Result->SetStringField(
897
+ TEXT("message"),
898
+ FString::Printf(TEXT("Property '%s' set on component '%s'"),
899
+ *PropertyName, *ComponentName));
900
+ Result->SetBoolField(TEXT("compiled"), bCompiled);
901
+ Result->SetBoolField(TEXT("saved"), bSaved);
902
+ #else
903
+ return UnsupportedSCSAction();
904
+ #endif
905
+
906
+ return Result;
907
+ }
908
+
909
+ #if !WITH_EDITOR
910
+ static TSharedPtr<FJsonObject> UnsupportedSCSAction() {
911
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
912
+ Result->SetBoolField(TEXT("success"), false);
913
+ Result->SetStringField(TEXT("error"),
914
+ TEXT("SCS operations require editor build"));
915
+ return Result;
916
+ }
917
+ #endif