unreal-engine-mcp-server 0.4.7 → 0.5.1

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 (454) 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-config.yml +51 -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 +27 -0
  19. package/.github/workflows/labeler.yml +17 -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 +13 -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 +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. 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