unreal-engine-mcp-server 0.5.4 → 0.5.6

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 (561) hide show
  1. package/CHANGELOG.md +350 -0
  2. package/dist/automation/bridge.d.ts.map +1 -0
  3. package/dist/automation/bridge.js +5 -4
  4. package/dist/automation/bridge.js.map +1 -0
  5. package/dist/automation/connection-manager.d.ts.map +1 -0
  6. package/dist/automation/connection-manager.js.map +1 -0
  7. package/dist/automation/handshake.d.ts.map +1 -0
  8. package/dist/automation/handshake.js.map +1 -0
  9. package/dist/automation/index.d.ts.map +1 -0
  10. package/dist/automation/index.js.map +1 -0
  11. package/dist/automation/message-handler.d.ts.map +1 -0
  12. package/dist/automation/message-handler.js.map +1 -0
  13. package/dist/automation/request-tracker.d.ts.map +1 -0
  14. package/dist/automation/request-tracker.js.map +1 -0
  15. package/dist/automation/types.d.ts +7 -0
  16. package/dist/automation/types.d.ts.map +1 -0
  17. package/dist/automation/types.js.map +1 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +6 -4
  20. package/dist/cli.js.map +1 -0
  21. package/dist/config/class-aliases.d.ts.map +1 -0
  22. package/dist/config/class-aliases.js.map +1 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js.map +1 -0
  25. package/dist/constants.d.ts.map +1 -0
  26. package/dist/constants.js.map +1 -0
  27. package/dist/graphql/loaders.d.ts.map +1 -0
  28. package/dist/graphql/loaders.js.map +1 -0
  29. package/dist/graphql/resolvers.d.ts +174 -69
  30. package/dist/graphql/resolvers.d.ts.map +1 -0
  31. package/dist/graphql/resolvers.js +82 -67
  32. package/dist/graphql/resolvers.js.map +1 -0
  33. package/dist/graphql/schema.d.ts.map +1 -0
  34. package/dist/graphql/schema.js.map +1 -0
  35. package/dist/graphql/server.d.ts.map +1 -0
  36. package/dist/graphql/server.js.map +1 -0
  37. package/dist/graphql/types.d.ts.map +1 -0
  38. package/dist/graphql/types.js.map +1 -0
  39. package/dist/handlers/resource-handlers.d.ts.map +1 -0
  40. package/dist/handlers/resource-handlers.js.map +1 -0
  41. package/dist/index.d.ts +2 -1
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +70 -9
  44. package/dist/index.js.map +1 -0
  45. package/dist/resources/actors.d.ts +7 -4
  46. package/dist/resources/actors.d.ts.map +1 -0
  47. package/dist/resources/actors.js +15 -12
  48. package/dist/resources/actors.js.map +1 -0
  49. package/dist/resources/assets.d.ts +43 -2
  50. package/dist/resources/assets.d.ts.map +1 -0
  51. package/dist/resources/assets.js +21 -12
  52. package/dist/resources/assets.js.map +1 -0
  53. package/dist/resources/levels.d.ts.map +1 -0
  54. package/dist/resources/levels.js +7 -5
  55. package/dist/resources/levels.js.map +1 -0
  56. package/dist/schemas/index.d.ts +4 -0
  57. package/dist/schemas/index.d.ts.map +1 -0
  58. package/dist/schemas/index.js +4 -0
  59. package/dist/schemas/index.js.map +1 -0
  60. package/dist/schemas/parser.d.ts +20 -0
  61. package/dist/schemas/parser.d.ts.map +1 -0
  62. package/dist/schemas/parser.js +61 -0
  63. package/dist/schemas/parser.js.map +1 -0
  64. package/dist/schemas/primitives.d.ts +221 -0
  65. package/dist/schemas/primitives.d.ts.map +1 -0
  66. package/dist/schemas/primitives.js +115 -0
  67. package/dist/schemas/primitives.js.map +1 -0
  68. package/dist/schemas/responses.d.ts +362 -0
  69. package/dist/schemas/responses.d.ts.map +1 -0
  70. package/dist/schemas/responses.js +252 -0
  71. package/dist/schemas/responses.js.map +1 -0
  72. package/dist/server/resource-registry.d.ts.map +1 -0
  73. package/dist/server/resource-registry.js.map +1 -0
  74. package/dist/server/tool-registry.d.ts.map +1 -0
  75. package/dist/server/tool-registry.js +22 -17
  76. package/dist/server/tool-registry.js.map +1 -0
  77. package/dist/server-setup.d.ts.map +1 -0
  78. package/dist/server-setup.js.map +1 -0
  79. package/dist/services/health-monitor.d.ts +1 -1
  80. package/dist/services/health-monitor.d.ts.map +1 -0
  81. package/dist/services/health-monitor.js +4 -3
  82. package/dist/services/health-monitor.js.map +1 -0
  83. package/dist/services/metrics-server.d.ts.map +1 -0
  84. package/dist/services/metrics-server.js.map +1 -0
  85. package/dist/tools/actors.d.ts +27 -27
  86. package/dist/tools/actors.d.ts.map +1 -0
  87. package/dist/tools/actors.js +14 -10
  88. package/dist/tools/actors.js.map +1 -0
  89. package/dist/tools/animation.d.ts +15 -23
  90. package/dist/tools/animation.d.ts.map +1 -0
  91. package/dist/tools/animation.js +17 -13
  92. package/dist/tools/animation.js.map +1 -0
  93. package/dist/tools/assets.d.ts.map +1 -0
  94. package/dist/tools/assets.js +18 -12
  95. package/dist/tools/assets.js.map +1 -0
  96. package/dist/tools/audio.d.ts +10 -10
  97. package/dist/tools/audio.d.ts.map +1 -0
  98. package/dist/tools/audio.js.map +1 -0
  99. package/dist/tools/base-tool.d.ts.map +1 -0
  100. package/dist/tools/base-tool.js.map +1 -0
  101. package/dist/tools/behavior-tree.d.ts +24 -24
  102. package/dist/tools/behavior-tree.d.ts.map +1 -0
  103. package/dist/tools/behavior-tree.js.map +1 -0
  104. package/dist/tools/blueprint.d.ts +14 -3
  105. package/dist/tools/blueprint.d.ts.map +1 -0
  106. package/dist/tools/blueprint.js +5 -3
  107. package/dist/tools/blueprint.js.map +1 -0
  108. package/dist/tools/consolidated-tool-definitions.d.ts +32 -32
  109. package/dist/tools/consolidated-tool-definitions.d.ts.map +1 -0
  110. package/dist/tools/consolidated-tool-definitions.js.map +1 -0
  111. package/dist/tools/consolidated-tool-handlers.d.ts +1 -1
  112. package/dist/tools/consolidated-tool-handlers.d.ts.map +1 -0
  113. package/dist/tools/consolidated-tool-handlers.js +26 -21
  114. package/dist/tools/consolidated-tool-handlers.js.map +1 -0
  115. package/dist/tools/debug.d.ts +25 -7
  116. package/dist/tools/debug.d.ts.map +1 -0
  117. package/dist/tools/debug.js +3 -1
  118. package/dist/tools/debug.js.map +1 -0
  119. package/dist/tools/dynamic-handler-registry.d.ts +1 -1
  120. package/dist/tools/dynamic-handler-registry.d.ts.map +1 -0
  121. package/dist/tools/dynamic-handler-registry.js +3 -1
  122. package/dist/tools/dynamic-handler-registry.js.map +1 -0
  123. package/dist/tools/editor.d.ts.map +1 -0
  124. package/dist/tools/editor.js +8 -6
  125. package/dist/tools/editor.js.map +1 -0
  126. package/dist/tools/engine.d.ts +1 -1
  127. package/dist/tools/engine.d.ts.map +1 -0
  128. package/dist/tools/engine.js +4 -2
  129. package/dist/tools/engine.js.map +1 -0
  130. package/dist/tools/environment.d.ts.map +1 -0
  131. package/dist/tools/environment.js +4 -3
  132. package/dist/tools/environment.js.map +1 -0
  133. package/dist/tools/foliage.d.ts.map +1 -0
  134. package/dist/tools/foliage.js +8 -8
  135. package/dist/tools/foliage.js.map +1 -0
  136. package/dist/tools/handlers/actor-handlers.d.ts +2 -1
  137. package/dist/tools/handlers/actor-handlers.d.ts.map +1 -0
  138. package/dist/tools/handlers/actor-handlers.js +56 -33
  139. package/dist/tools/handlers/actor-handlers.js.map +1 -0
  140. package/dist/tools/handlers/animation-handlers.d.ts +2 -1
  141. package/dist/tools/handlers/animation-handlers.d.ts.map +1 -0
  142. package/dist/tools/handlers/animation-handlers.js +74 -67
  143. package/dist/tools/handlers/animation-handlers.js.map +1 -0
  144. package/dist/tools/handlers/argument-helper.d.ts +24 -4
  145. package/dist/tools/handlers/argument-helper.d.ts.map +1 -0
  146. package/dist/tools/handlers/argument-helper.js +139 -4
  147. package/dist/tools/handlers/argument-helper.js.map +1 -0
  148. package/dist/tools/handlers/asset-handlers.d.ts +2 -1
  149. package/dist/tools/handlers/asset-handlers.d.ts.map +1 -0
  150. package/dist/tools/handlers/asset-handlers.js +155 -94
  151. package/dist/tools/handlers/asset-handlers.js.map +1 -0
  152. package/dist/tools/handlers/audio-handlers.d.ts +2 -1
  153. package/dist/tools/handlers/audio-handlers.d.ts.map +1 -0
  154. package/dist/tools/handlers/audio-handlers.js +82 -80
  155. package/dist/tools/handlers/audio-handlers.js.map +1 -0
  156. package/dist/tools/handlers/blueprint-handlers.d.ts +3 -5
  157. package/dist/tools/handlers/blueprint-handlers.d.ts.map +1 -0
  158. package/dist/tools/handlers/blueprint-handlers.js +150 -142
  159. package/dist/tools/handlers/blueprint-handlers.js.map +1 -0
  160. package/dist/tools/handlers/common-handlers.d.ts +2 -3
  161. package/dist/tools/handlers/common-handlers.d.ts.map +1 -0
  162. package/dist/tools/handlers/common-handlers.js.map +1 -0
  163. package/dist/tools/handlers/editor-handlers.d.ts.map +1 -0
  164. package/dist/tools/handlers/editor-handlers.js +12 -2
  165. package/dist/tools/handlers/editor-handlers.js.map +1 -0
  166. package/dist/tools/handlers/effect-handlers.d.ts +2 -1
  167. package/dist/tools/handlers/effect-handlers.d.ts.map +1 -0
  168. package/dist/tools/handlers/effect-handlers.js +70 -68
  169. package/dist/tools/handlers/effect-handlers.js.map +1 -0
  170. package/dist/tools/handlers/environment-handlers.d.ts +2 -1
  171. package/dist/tools/handlers/environment-handlers.d.ts.map +1 -0
  172. package/dist/tools/handlers/environment-handlers.js +86 -74
  173. package/dist/tools/handlers/environment-handlers.js.map +1 -0
  174. package/dist/tools/handlers/graph-handlers.d.ts +1 -1
  175. package/dist/tools/handlers/graph-handlers.d.ts.map +1 -0
  176. package/dist/tools/handlers/graph-handlers.js +63 -2
  177. package/dist/tools/handlers/graph-handlers.js.map +1 -0
  178. package/dist/tools/handlers/input-handlers.d.ts +2 -5
  179. package/dist/tools/handlers/input-handlers.d.ts.map +1 -0
  180. package/dist/tools/handlers/input-handlers.js +5 -4
  181. package/dist/tools/handlers/input-handlers.js.map +1 -0
  182. package/dist/tools/handlers/inspect-handlers.d.ts +2 -1
  183. package/dist/tools/handlers/inspect-handlers.d.ts.map +1 -0
  184. package/dist/tools/handlers/inspect-handlers.js +61 -37
  185. package/dist/tools/handlers/inspect-handlers.js.map +1 -0
  186. package/dist/tools/handlers/level-handlers.d.ts +2 -2
  187. package/dist/tools/handlers/level-handlers.d.ts.map +1 -0
  188. package/dist/tools/handlers/level-handlers.js +43 -39
  189. package/dist/tools/handlers/level-handlers.js.map +1 -0
  190. package/dist/tools/handlers/lighting-handlers.d.ts +12 -1
  191. package/dist/tools/handlers/lighting-handlers.d.ts.map +1 -0
  192. package/dist/tools/handlers/lighting-handlers.js +90 -47
  193. package/dist/tools/handlers/lighting-handlers.js.map +1 -0
  194. package/dist/tools/handlers/performance-handlers.d.ts +2 -1
  195. package/dist/tools/handlers/performance-handlers.d.ts.map +1 -0
  196. package/dist/tools/handlers/performance-handlers.js +55 -40
  197. package/dist/tools/handlers/performance-handlers.js.map +1 -0
  198. package/dist/tools/handlers/pipeline-handlers.d.ts.map +1 -0
  199. package/dist/tools/handlers/pipeline-handlers.js.map +1 -0
  200. package/dist/tools/handlers/sequence-handlers.d.ts.map +1 -0
  201. package/dist/tools/handlers/sequence-handlers.js.map +1 -0
  202. package/dist/tools/handlers/system-handlers.d.ts +3 -2
  203. package/dist/tools/handlers/system-handlers.d.ts.map +1 -0
  204. package/dist/tools/handlers/system-handlers.js +105 -52
  205. package/dist/tools/handlers/system-handlers.js.map +1 -0
  206. package/dist/tools/input.d.ts.map +1 -0
  207. package/dist/tools/input.js +3 -1
  208. package/dist/tools/input.js.map +1 -0
  209. package/dist/tools/introspection.d.ts +14 -14
  210. package/dist/tools/introspection.d.ts.map +1 -0
  211. package/dist/tools/introspection.js +54 -45
  212. package/dist/tools/introspection.js.map +1 -0
  213. package/dist/tools/landscape.d.ts.map +1 -0
  214. package/dist/tools/landscape.js +15 -13
  215. package/dist/tools/landscape.js.map +1 -0
  216. package/dist/tools/level.d.ts.map +1 -0
  217. package/dist/tools/level.js +3 -2
  218. package/dist/tools/level.js.map +1 -0
  219. package/dist/tools/lighting.d.ts +32 -59
  220. package/dist/tools/lighting.d.ts.map +1 -0
  221. package/dist/tools/lighting.js +56 -19
  222. package/dist/tools/lighting.js.map +1 -0
  223. package/dist/tools/logs.d.ts.map +1 -0
  224. package/dist/tools/logs.js +2 -1
  225. package/dist/tools/logs.js.map +1 -0
  226. package/dist/tools/materials.d.ts +42 -14
  227. package/dist/tools/materials.d.ts.map +1 -0
  228. package/dist/tools/materials.js +15 -9
  229. package/dist/tools/materials.js.map +1 -0
  230. package/dist/tools/niagara.d.ts +63 -39
  231. package/dist/tools/niagara.d.ts.map +1 -0
  232. package/dist/tools/niagara.js +43 -33
  233. package/dist/tools/niagara.js.map +1 -0
  234. package/dist/tools/performance.d.ts +12 -11
  235. package/dist/tools/performance.d.ts.map +1 -0
  236. package/dist/tools/performance.js +3 -2
  237. package/dist/tools/performance.js.map +1 -0
  238. package/dist/tools/physics.d.ts +37 -20
  239. package/dist/tools/physics.d.ts.map +1 -0
  240. package/dist/tools/physics.js +37 -30
  241. package/dist/tools/physics.js.map +1 -0
  242. package/dist/tools/property-dictionary.d.ts.map +1 -0
  243. package/dist/tools/property-dictionary.js.map +1 -0
  244. package/dist/tools/sequence.d.ts +1 -1
  245. package/dist/tools/sequence.d.ts.map +1 -0
  246. package/dist/tools/sequence.js +8 -4
  247. package/dist/tools/sequence.js.map +1 -0
  248. package/dist/tools/tool-definition-utils.d.ts.map +1 -0
  249. package/dist/tools/tool-definition-utils.js.map +1 -0
  250. package/dist/tools/ui.d.ts +11 -11
  251. package/dist/tools/ui.d.ts.map +1 -0
  252. package/dist/tools/ui.js +7 -3
  253. package/dist/tools/ui.js.map +1 -0
  254. package/dist/types/automation-responses.d.ts.map +1 -0
  255. package/dist/types/automation-responses.js.map +1 -0
  256. package/dist/types/env.d.ts.map +1 -0
  257. package/dist/types/env.js.map +1 -0
  258. package/dist/types/handler-types.d.ts +112 -3
  259. package/dist/types/handler-types.d.ts.map +1 -0
  260. package/dist/types/handler-types.js.map +1 -0
  261. package/dist/types/tool-interfaces.d.ts +39 -21
  262. package/dist/types/tool-interfaces.d.ts.map +1 -0
  263. package/dist/types/tool-interfaces.js.map +1 -0
  264. package/dist/types/tool-types.d.ts +8 -8
  265. package/dist/types/tool-types.d.ts.map +1 -0
  266. package/dist/types/tool-types.js.map +1 -0
  267. package/dist/unreal-bridge.d.ts +8 -6
  268. package/dist/unreal-bridge.d.ts.map +1 -0
  269. package/dist/unreal-bridge.js +16 -3
  270. package/dist/unreal-bridge.js.map +1 -0
  271. package/dist/utils/command-validator.d.ts.map +1 -0
  272. package/dist/utils/command-validator.js.map +1 -0
  273. package/dist/utils/elicitation.d.ts +2 -5
  274. package/dist/utils/elicitation.d.ts.map +1 -0
  275. package/dist/utils/elicitation.js +3 -2
  276. package/dist/utils/elicitation.js.map +1 -0
  277. package/dist/utils/error-handler.d.ts.map +1 -0
  278. package/dist/utils/error-handler.js.map +1 -0
  279. package/dist/utils/ini-reader.d.ts +1 -1
  280. package/dist/utils/ini-reader.d.ts.map +1 -0
  281. package/dist/utils/ini-reader.js.map +1 -0
  282. package/dist/utils/logger.d.ts +4 -4
  283. package/dist/utils/logger.d.ts.map +1 -0
  284. package/dist/utils/logger.js.map +1 -0
  285. package/dist/utils/normalize.d.ts +2 -2
  286. package/dist/utils/normalize.d.ts.map +1 -0
  287. package/dist/utils/normalize.js +4 -3
  288. package/dist/utils/normalize.js.map +1 -0
  289. package/dist/utils/path-security.d.ts.map +1 -0
  290. package/dist/utils/path-security.js.map +1 -0
  291. package/dist/utils/response-factory.d.ts +2 -2
  292. package/dist/utils/response-factory.d.ts.map +1 -0
  293. package/dist/utils/response-factory.js +3 -1
  294. package/dist/utils/response-factory.js.map +1 -0
  295. package/dist/utils/response-validator.d.ts +4 -4
  296. package/dist/utils/response-validator.d.ts.map +1 -0
  297. package/dist/utils/response-validator.js +31 -23
  298. package/dist/utils/response-validator.js.map +1 -0
  299. package/dist/utils/result-helpers.d.ts.map +1 -0
  300. package/dist/utils/result-helpers.js.map +1 -0
  301. package/dist/utils/safe-json.d.ts.map +1 -0
  302. package/dist/utils/safe-json.js.map +1 -0
  303. package/dist/utils/unreal-command-queue.d.ts +2 -2
  304. package/dist/utils/unreal-command-queue.d.ts.map +1 -0
  305. package/dist/utils/unreal-command-queue.js +4 -3
  306. package/dist/utils/unreal-command-queue.js.map +1 -0
  307. package/dist/utils/validation.d.ts +1 -1
  308. package/dist/utils/validation.d.ts.map +1 -0
  309. package/dist/utils/validation.js.map +1 -0
  310. package/dist/wasm/index.d.ts +2 -2
  311. package/dist/wasm/index.d.ts.map +1 -0
  312. package/dist/wasm/index.js +11 -7
  313. package/dist/wasm/index.js.map +1 -0
  314. package/package.json +12 -34
  315. package/server.json +2 -2
  316. package/.dockerignore +0 -57
  317. package/.env.example +0 -26
  318. package/.env.production +0 -61
  319. package/.eslintrc.json +0 -0
  320. package/.eslintrc.override.json +0 -8
  321. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -94
  322. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  323. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -56
  324. package/.github/copilot-instructions.md +0 -478
  325. package/.github/dependabot.yml +0 -19
  326. package/.github/labeler.yml +0 -24
  327. package/.github/labels.yml +0 -70
  328. package/.github/pull_request_template.md +0 -42
  329. package/.github/release-drafter-config.yml +0 -51
  330. package/.github/workflows/auto-merge.yml +0 -38
  331. package/.github/workflows/ci.yml +0 -38
  332. package/.github/workflows/dependency-review.yml +0 -17
  333. package/.github/workflows/gemini-issue-triage.yml +0 -172
  334. package/.github/workflows/greetings.yml +0 -27
  335. package/.github/workflows/labeler.yml +0 -17
  336. package/.github/workflows/links.yml +0 -80
  337. package/.github/workflows/pr-size-labeler.yml +0 -137
  338. package/.github/workflows/publish-mcp.yml +0 -79
  339. package/.github/workflows/release-drafter.yml +0 -24
  340. package/.github/workflows/release.yml +0 -112
  341. package/.github/workflows/semantic-pull-request.yml +0 -35
  342. package/.github/workflows/smoke-test.yml +0 -36
  343. package/.github/workflows/stale.yml +0 -28
  344. package/CONTRIBUTING.md +0 -140
  345. package/Dockerfile +0 -37
  346. package/GEMINI.md +0 -115
  347. package/Public/Plugin_setup_guide.mp4 +0 -0
  348. package/Public/icon.png +0 -0
  349. package/claude_desktop_config_example.json +0 -15
  350. package/dist/types/responses.d.ts +0 -249
  351. package/dist/types/responses.js +0 -2
  352. package/docs/GraphQL-API.md +0 -888
  353. package/docs/Migration-Guide-v0.5.0.md +0 -684
  354. package/docs/Roadmap.md +0 -53
  355. package/docs/WebAssembly-Integration.md +0 -628
  356. package/docs/editor-plugin-extension.md +0 -370
  357. package/docs/handler-mapping.md +0 -249
  358. package/docs/native-automation-progress.md +0 -128
  359. package/docs/testing-guide.md +0 -423
  360. package/eslint.config.mjs +0 -68
  361. package/mcp-config-example.json +0 -14
  362. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +0 -8
  363. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +0 -64
  364. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +0 -189
  365. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +0 -22
  366. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +0 -30
  367. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +0 -1983
  368. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +0 -72
  369. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +0 -46
  370. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +0 -846
  371. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +0 -2393
  372. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +0 -300
  373. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +0 -2807
  374. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +0 -1087
  375. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +0 -488
  376. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +0 -643
  377. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +0 -31
  378. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +0 -1094
  379. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +0 -5750
  380. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +0 -152
  381. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +0 -2614
  382. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +0 -42
  383. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +0 -1237
  384. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +0 -1725
  385. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +0 -2265
  386. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +0 -954
  387. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +0 -209
  388. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +0 -41
  389. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +0 -1164
  390. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +0 -762
  391. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +0 -663
  392. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +0 -136
  393. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +0 -494
  394. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +0 -278
  395. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +0 -625
  396. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +0 -401
  397. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +0 -67
  398. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +0 -472
  399. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +0 -2634
  400. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +0 -189
  401. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +0 -917
  402. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +0 -39
  403. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +0 -2706
  404. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +0 -519
  405. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +0 -38
  406. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +0 -668
  407. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +0 -346
  408. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +0 -1345
  409. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +0 -149
  410. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -782
  411. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +0 -115
  412. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +0 -796
  413. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +0 -117
  414. package/scripts/check-unreal-connection.mjs +0 -19
  415. package/scripts/clean-tmp.js +0 -23
  416. package/scripts/patch-wasm.js +0 -26
  417. package/scripts/run-all-tests.mjs +0 -136
  418. package/scripts/smoke-test.ts +0 -94
  419. package/scripts/sync-mcp-plugin.js +0 -143
  420. package/scripts/test-no-plugin-alternates.mjs +0 -113
  421. package/scripts/validate-server.js +0 -46
  422. package/scripts/verify-automation-bridge.js +0 -200
  423. package/src/automation/bridge.ts +0 -630
  424. package/src/automation/connection-manager.ts +0 -148
  425. package/src/automation/handshake.ts +0 -99
  426. package/src/automation/index.ts +0 -2
  427. package/src/automation/message-handler.ts +0 -192
  428. package/src/automation/request-tracker.ts +0 -155
  429. package/src/automation/types.ts +0 -108
  430. package/src/cli.ts +0 -34
  431. package/src/config/class-aliases.ts +0 -65
  432. package/src/config.ts +0 -73
  433. package/src/constants.ts +0 -29
  434. package/src/graphql/loaders.ts +0 -244
  435. package/src/graphql/resolvers.ts +0 -1008
  436. package/src/graphql/schema.ts +0 -452
  437. package/src/graphql/server.ts +0 -156
  438. package/src/graphql/types.ts +0 -10
  439. package/src/handlers/resource-handlers.ts +0 -186
  440. package/src/index.ts +0 -243
  441. package/src/resources/actors.ts +0 -127
  442. package/src/resources/assets.ts +0 -286
  443. package/src/resources/levels.ts +0 -68
  444. package/src/server/resource-registry.ts +0 -47
  445. package/src/server/tool-registry.ts +0 -354
  446. package/src/server-setup.ts +0 -114
  447. package/src/services/health-monitor.ts +0 -132
  448. package/src/services/metrics-server.ts +0 -176
  449. package/src/tools/actors.ts +0 -564
  450. package/src/tools/animation.ts +0 -941
  451. package/src/tools/assets.ts +0 -394
  452. package/src/tools/audio.ts +0 -499
  453. package/src/tools/base-tool.ts +0 -52
  454. package/src/tools/behavior-tree.ts +0 -45
  455. package/src/tools/blueprint.ts +0 -940
  456. package/src/tools/consolidated-tool-definitions.ts +0 -1256
  457. package/src/tools/consolidated-tool-handlers.ts +0 -302
  458. package/src/tools/debug.ts +0 -622
  459. package/src/tools/dynamic-handler-registry.ts +0 -33
  460. package/src/tools/editor.ts +0 -435
  461. package/src/tools/engine.ts +0 -43
  462. package/src/tools/environment.ts +0 -281
  463. package/src/tools/foliage.ts +0 -596
  464. package/src/tools/handlers/actor-handlers.ts +0 -244
  465. package/src/tools/handlers/animation-handlers.ts +0 -237
  466. package/src/tools/handlers/argument-helper.ts +0 -142
  467. package/src/tools/handlers/asset-handlers.ts +0 -550
  468. package/src/tools/handlers/audio-handlers.ts +0 -194
  469. package/src/tools/handlers/blueprint-handlers.ts +0 -380
  470. package/src/tools/handlers/common-handlers.ts +0 -108
  471. package/src/tools/handlers/editor-handlers.ts +0 -124
  472. package/src/tools/handlers/effect-handlers.ts +0 -224
  473. package/src/tools/handlers/environment-handlers.ts +0 -183
  474. package/src/tools/handlers/graph-handlers.ts +0 -117
  475. package/src/tools/handlers/input-handlers.ts +0 -28
  476. package/src/tools/handlers/inspect-handlers.ts +0 -450
  477. package/src/tools/handlers/level-handlers.ts +0 -253
  478. package/src/tools/handlers/lighting-handlers.ts +0 -151
  479. package/src/tools/handlers/performance-handlers.ts +0 -132
  480. package/src/tools/handlers/pipeline-handlers.ts +0 -194
  481. package/src/tools/handlers/sequence-handlers.ts +0 -438
  482. package/src/tools/handlers/system-handlers.ts +0 -564
  483. package/src/tools/input.ts +0 -160
  484. package/src/tools/introspection.ts +0 -689
  485. package/src/tools/landscape.ts +0 -649
  486. package/src/tools/level.ts +0 -989
  487. package/src/tools/lighting.ts +0 -1052
  488. package/src/tools/logs.ts +0 -219
  489. package/src/tools/materials.ts +0 -295
  490. package/src/tools/niagara.ts +0 -485
  491. package/src/tools/performance.ts +0 -661
  492. package/src/tools/physics.ts +0 -679
  493. package/src/tools/property-dictionary.ts +0 -98
  494. package/src/tools/sequence.ts +0 -385
  495. package/src/tools/tool-definition-utils.ts +0 -35
  496. package/src/tools/ui.ts +0 -452
  497. package/src/types/automation-responses.ts +0 -119
  498. package/src/types/env.ts +0 -17
  499. package/src/types/handler-types.ts +0 -442
  500. package/src/types/responses.ts +0 -355
  501. package/src/types/tool-interfaces.ts +0 -250
  502. package/src/types/tool-types.ts +0 -575
  503. package/src/unreal-bridge.ts +0 -693
  504. package/src/utils/command-validator.ts +0 -139
  505. package/src/utils/elicitation.ts +0 -132
  506. package/src/utils/error-handler.ts +0 -287
  507. package/src/utils/ini-reader.ts +0 -86
  508. package/src/utils/logger.ts +0 -35
  509. package/src/utils/normalize.test.ts +0 -162
  510. package/src/utils/normalize.ts +0 -146
  511. package/src/utils/path-security.ts +0 -43
  512. package/src/utils/response-factory.ts +0 -44
  513. package/src/utils/response-validator.ts +0 -395
  514. package/src/utils/result-helpers.ts +0 -195
  515. package/src/utils/safe-json.test.ts +0 -90
  516. package/src/utils/safe-json.ts +0 -70
  517. package/src/utils/unreal-command-queue.ts +0 -166
  518. package/src/utils/validation.test.ts +0 -184
  519. package/src/utils/validation.ts +0 -312
  520. package/src/wasm/index.ts +0 -838
  521. package/test-server.mjs +0 -100
  522. package/tests/test-animation.mjs +0 -369
  523. package/tests/test-asset-advanced.mjs +0 -82
  524. package/tests/test-asset-graph.mjs +0 -311
  525. package/tests/test-audio.mjs +0 -417
  526. package/tests/test-automation-timeouts.mjs +0 -98
  527. package/tests/test-behavior-tree.mjs +0 -444
  528. package/tests/test-blueprint-graph.mjs +0 -410
  529. package/tests/test-blueprint.mjs +0 -577
  530. package/tests/test-client-mode.mjs +0 -86
  531. package/tests/test-console-command.mjs +0 -56
  532. package/tests/test-control-actor.mjs +0 -425
  533. package/tests/test-control-editor.mjs +0 -112
  534. package/tests/test-graphql.mjs +0 -372
  535. package/tests/test-input.mjs +0 -349
  536. package/tests/test-inspect.mjs +0 -302
  537. package/tests/test-landscape.mjs +0 -316
  538. package/tests/test-lighting.mjs +0 -428
  539. package/tests/test-manage-asset.mjs +0 -438
  540. package/tests/test-manage-level.mjs +0 -89
  541. package/tests/test-materials.mjs +0 -356
  542. package/tests/test-niagara.mjs +0 -185
  543. package/tests/test-no-inline-python.mjs +0 -122
  544. package/tests/test-performance.mjs +0 -539
  545. package/tests/test-plugin-handshake.mjs +0 -82
  546. package/tests/test-runner.mjs +0 -993
  547. package/tests/test-sequence.mjs +0 -104
  548. package/tests/test-system.mjs +0 -96
  549. package/tests/test-wasm.mjs +0 -283
  550. package/tests/test-world-partition.mjs +0 -215
  551. package/tsconfig.json +0 -56
  552. package/vitest.config.ts +0 -35
  553. package/wasm/Cargo.lock +0 -363
  554. package/wasm/Cargo.toml +0 -42
  555. package/wasm/LICENSE +0 -21
  556. package/wasm/README.md +0 -253
  557. package/wasm/src/dependency_resolver.rs +0 -377
  558. package/wasm/src/lib.rs +0 -153
  559. package/wasm/src/property_parser.rs +0 -271
  560. package/wasm/src/transform_math.rs +0 -396
  561. package/wasm/tests/integration.rs +0 -109
@@ -1,1725 +0,0 @@
1
- #include "DrawDebugHelpers.h"
2
- #include "McpAutomationBridgeGlobals.h"
3
- #include "McpAutomationBridgeHelpers.h"
4
- #include "McpAutomationBridgeSubsystem.h"
5
-
6
- #if WITH_EDITOR
7
- #include "EditorAssetLibrary.h"
8
- #if __has_include("Subsystems/EditorActorSubsystem.h")
9
- #include "Subsystems/EditorActorSubsystem.h"
10
- #elif __has_include("EditorActorSubsystem.h")
11
- #include "EditorActorSubsystem.h"
12
- #endif
13
- #if __has_include("NiagaraActor.h")
14
- #include "NiagaraActor.h"
15
- #endif
16
- #if __has_include("NiagaraComponent.h")
17
- #include "NiagaraComponent.h"
18
- #endif
19
- #if __has_include("NiagaraSystem.h")
20
- #include "NiagaraFunctionLibrary.h"
21
- #include "NiagaraSystem.h"
22
- #include "NiagaraSystemFactoryNew.h"
23
- #endif
24
- #if __has_include("Engine/PointLight.h")
25
- #include "Engine/PointLight.h"
26
- #endif
27
- #if __has_include("Engine/SpotLight.h")
28
- #include "Engine/SpotLight.h"
29
- #endif
30
- #if __has_include("Engine/DirectionalLight.h")
31
- #include "Engine/DirectionalLight.h"
32
- #endif
33
- #if __has_include("Engine/RectLight.h")
34
- #include "Engine/RectLight.h"
35
- #endif
36
- #if __has_include("Components/LightComponent.h")
37
- #include "Components/LightComponent.h"
38
- #endif
39
- #if __has_include("Components/PointLightComponent.h")
40
- #include "Components/PointLightComponent.h"
41
- #endif
42
- #if __has_include("Components/SpotLightComponent.h")
43
- #include "Components/SpotLightComponent.h"
44
- #endif
45
- #if __has_include("Components/RectLightComponent.h")
46
- #include "Components/RectLightComponent.h"
47
- #endif
48
- #if __has_include("Components/DirectionalLightComponent.h")
49
- #include "Components/DirectionalLightComponent.h"
50
- #endif
51
- #endif
52
-
53
- bool UMcpAutomationBridgeSubsystem::HandleEffectAction(
54
- const FString &RequestId, const FString &Action,
55
- const TSharedPtr<FJsonObject> &Payload,
56
- TSharedPtr<FMcpBridgeWebSocket> RequestingSocket) {
57
- const FString Lower = Action.ToLower();
58
- const bool bIsCreateEffect = Lower.Equals(TEXT("create_effect")) ||
59
- Lower.StartsWith(TEXT("create_effect"));
60
- if (!bIsCreateEffect && !Lower.StartsWith(TEXT("spawn_")) &&
61
- !Lower.Equals(TEXT("set_niagara_parameter")) &&
62
- !Lower.Equals(TEXT("list_debug_shapes")) &&
63
- !Lower.Equals(TEXT("clear_debug_shapes")))
64
- return false;
65
-
66
- TSharedPtr<FJsonObject> LocalPayload =
67
- Payload.IsValid() ? Payload : MakeShared<FJsonObject>();
68
-
69
- auto SendResponse = [&](bool bOk, const FString &Msg,
70
- const TSharedPtr<FJsonObject> &ResObj,
71
- const FString &ErrCode = FString()) {
72
- SendAutomationResponse(RequestingSocket, RequestId, bOk, Msg, ResObj,
73
- ErrCode);
74
- };
75
-
76
- // Discovery: list available debug shape types
77
- if (Lower.Equals(TEXT("list_debug_shapes"))) {
78
- TArray<TSharedPtr<FJsonValue>> Shapes;
79
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("sphere")));
80
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("box")));
81
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("circle")));
82
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("line")));
83
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("point")));
84
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("coordinate")));
85
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("cylinder")));
86
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("cone")));
87
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("capsule")));
88
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("arrow")));
89
- Shapes.Add(MakeShared<FJsonValueString>(TEXT("plane")));
90
-
91
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
92
- Resp->SetArrayField(TEXT("shapes"), Shapes);
93
- Resp->SetNumberField(TEXT("count"), Shapes.Num());
94
- SendAutomationResponse(RequestingSocket, RequestId, true,
95
- TEXT("Available debug shape types"), Resp);
96
- return true;
97
- }
98
-
99
- // Handle create_effect tool with sub-actions
100
- if (Lower.Equals(TEXT("clear_debug_shapes"))) {
101
- #if WITH_EDITOR
102
- if (GEditor && GEditor->GetEditorWorldContext().World()) {
103
- FlushPersistentDebugLines(GEditor->GetEditorWorldContext().World());
104
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
105
- Resp->SetBoolField(TEXT("success"), true);
106
- SendAutomationResponse(RequestingSocket, RequestId, true,
107
- TEXT("Debug shapes cleared"), Resp);
108
- return true;
109
- } else {
110
- SendAutomationResponse(RequestingSocket, RequestId, false,
111
- TEXT("Editor world not available"), nullptr,
112
- TEXT("NO_WORLD"));
113
- return true;
114
- }
115
- #else
116
- SendAutomationResponse(RequestingSocket, RequestId, false,
117
- TEXT("Debug shape clearing requires editor build"),
118
- nullptr, TEXT("NOT_IMPLEMENTED"));
119
- return true;
120
- #endif
121
- }
122
-
123
- if (bIsCreateEffect || Lower.Equals(TEXT("create_niagara_system"))) {
124
- FString SubAction;
125
- LocalPayload->TryGetStringField(TEXT("action"), SubAction);
126
-
127
- if (Lower.Equals(TEXT("create_niagara_system"))) {
128
- SubAction = TEXT("create_niagara_system");
129
- }
130
-
131
- // Fallback: if action field in payload is empty, check if the top-level
132
- // Action is a specific tool (e.g. set_niagara_parameter) and use that as
133
- // sub-action.
134
- if (SubAction.IsEmpty() &&
135
- !Action.Equals(TEXT("create_effect"), ESearchCase::IgnoreCase)) {
136
- SubAction = Action;
137
- }
138
-
139
- const FString LowerSub = SubAction.ToLower();
140
-
141
- // Handle particle spawning
142
- if (LowerSub == TEXT("particle")) {
143
- FString Preset;
144
- LocalPayload->TryGetStringField(TEXT("preset"), Preset);
145
- if (Preset.IsEmpty()) {
146
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
147
- Resp->SetBoolField(TEXT("success"), false);
148
- Resp->SetStringField(
149
- TEXT("error"),
150
- TEXT("preset parameter required for particle spawning"));
151
- SendAutomationResponse(RequestingSocket, RequestId, false,
152
- TEXT("Preset path required"), Resp,
153
- TEXT("INVALID_ARGUMENT"));
154
- return true;
155
- }
156
-
157
- // Location and optional rotation/scale
158
- FVector Loc(0, 0, 0);
159
- if (LocalPayload->HasField(TEXT("location"))) {
160
- const TSharedPtr<FJsonValue> LocVal =
161
- LocalPayload->TryGetField(TEXT("location"));
162
- if (LocVal.IsValid()) {
163
- if (LocVal->Type == EJson::Array) {
164
- const TArray<TSharedPtr<FJsonValue>> &Arr = LocVal->AsArray();
165
- if (Arr.Num() >= 3)
166
- Loc =
167
- FVector((float)Arr[0]->AsNumber(), (float)Arr[1]->AsNumber(),
168
- (float)Arr[2]->AsNumber());
169
- } else if (LocVal->Type == EJson::Object) {
170
- const TSharedPtr<FJsonObject> O = LocVal->AsObject();
171
- if (O.IsValid())
172
- Loc = FVector(
173
- (float)(O->HasField(TEXT("x")) ? O->GetNumberField(TEXT("x"))
174
- : 0.0),
175
- (float)(O->HasField(TEXT("y")) ? O->GetNumberField(TEXT("y"))
176
- : 0.0),
177
- (float)(O->HasField(TEXT("z")) ? O->GetNumberField(TEXT("z"))
178
- : 0.0));
179
- }
180
- }
181
- }
182
-
183
- // Rotation may be an array
184
- TArray<double> RotArr = {0, 0, 0};
185
- const TArray<TSharedPtr<FJsonValue>> *RA = nullptr;
186
- if (LocalPayload->TryGetArrayField(TEXT("rotation"), RA) && RA &&
187
- RA->Num() >= 3) {
188
- RotArr[0] = (*RA)[0]->AsNumber();
189
- RotArr[1] = (*RA)[1]->AsNumber();
190
- RotArr[2] = (*RA)[2]->AsNumber();
191
- }
192
-
193
- // Scale may be an array or a single numeric value
194
- TArray<double> ScaleArr = {1, 1, 1};
195
- const TArray<TSharedPtr<FJsonValue>> *ScaleJsonArr = nullptr;
196
- if (LocalPayload->TryGetArrayField(TEXT("scale"), ScaleJsonArr) &&
197
- ScaleJsonArr && ScaleJsonArr->Num() >= 3) {
198
- ScaleArr[0] = (*ScaleJsonArr)[0]->AsNumber();
199
- ScaleArr[1] = (*ScaleJsonArr)[1]->AsNumber();
200
- ScaleArr[2] = (*ScaleJsonArr)[2]->AsNumber();
201
- } else if (LocalPayload->TryGetNumberField(TEXT("scale"), ScaleArr[0])) {
202
- ScaleArr[1] = ScaleArr[2] = ScaleArr[0];
203
- }
204
-
205
- const bool bAutoDestroy =
206
- LocalPayload->HasField(TEXT("autoDestroy"))
207
- ? LocalPayload->GetBoolField(TEXT("autoDestroy"))
208
- : false;
209
-
210
- #if WITH_EDITOR
211
- if (!GEditor) {
212
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
213
- Resp->SetBoolField(TEXT("success"), false);
214
- Resp->SetStringField(TEXT("error"), TEXT("Editor not available"));
215
- SendAutomationResponse(RequestingSocket, RequestId, false,
216
- TEXT("Editor not available"), Resp,
217
- TEXT("EDITOR_NOT_AVAILABLE"));
218
- return true;
219
- }
220
- UEditorActorSubsystem *ActorSS =
221
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
222
- if (!ActorSS) {
223
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
224
- Resp->SetBoolField(TEXT("success"), false);
225
- Resp->SetStringField(TEXT("error"),
226
- TEXT("EditorActorSubsystem not available"));
227
- SendAutomationResponse(RequestingSocket, RequestId, false,
228
- TEXT("EditorActorSubsystem not available"), Resp,
229
- TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
230
- return true;
231
- }
232
-
233
- UObject *ParticleObj = UEditorAssetLibrary::LoadAsset(Preset);
234
- if (!ParticleObj) {
235
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
236
- Resp->SetBoolField(TEXT("success"), false);
237
- Resp->SetStringField(TEXT("error"),
238
- TEXT("Particle preset asset not found"));
239
- Resp->SetStringField(TEXT("preset"), Preset);
240
- SendAutomationResponse(RequestingSocket, RequestId, false,
241
- TEXT("Particle preset not found"), Resp,
242
- TEXT("PRESET_NOT_FOUND"));
243
- return true;
244
- }
245
-
246
- const FRotator SpawnRot(static_cast<float>(RotArr[0]),
247
- static_cast<float>(RotArr[1]),
248
- static_cast<float>(RotArr[2]));
249
- AActor *Spawned = SpawnActorInActiveWorld<AActor>(
250
- ANiagaraActor::StaticClass(), Loc, SpawnRot);
251
- if (!Spawned) {
252
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
253
- Resp->SetBoolField(TEXT("success"), false);
254
- Resp->SetStringField(TEXT("error"),
255
- TEXT("Failed to spawn particle actor"));
256
- SendAutomationResponse(RequestingSocket, RequestId, false,
257
- TEXT("Failed to spawn particle actor"), Resp,
258
- TEXT("SPAWN_FAILED"));
259
- return true;
260
- }
261
-
262
- UNiagaraComponent *NiComp =
263
- Spawned->FindComponentByClass<UNiagaraComponent>();
264
- if (NiComp && ParticleObj->IsA<UNiagaraSystem>()) {
265
- NiComp->SetAsset(Cast<UNiagaraSystem>(ParticleObj));
266
- NiComp->SetWorldScale3D(FVector(ScaleArr[0], ScaleArr[1], ScaleArr[2]));
267
- NiComp->Activate(true);
268
- }
269
-
270
- Spawned->SetActorLabel(FString::Printf(
271
- TEXT("Particle_%s_%lld"), *FPackageName::GetShortName(Preset),
272
- FDateTime::Now().ToUnixTimestamp()));
273
-
274
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
275
- Resp->SetBoolField(TEXT("success"), true);
276
- Resp->SetStringField(TEXT("particlePath"), Preset);
277
- Resp->SetStringField(TEXT("actorName"), Spawned->GetActorLabel());
278
- Resp->SetNumberField(TEXT("actorId"), Spawned->GetUniqueID());
279
- SendAutomationResponse(RequestingSocket, RequestId, true,
280
- TEXT("Particle preset spawned"), Resp, FString());
281
- return true;
282
- #else
283
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
284
- Resp->SetBoolField(TEXT("success"), false);
285
- Resp->SetStringField(TEXT("error"),
286
- TEXT("Particle spawning requires editor build"));
287
- Resp->SetStringField(TEXT("preset"), Preset);
288
- SendAutomationResponse(
289
- RequestingSocket, RequestId, false,
290
- TEXT("Particle spawning not available in non-editor build"), Resp,
291
- TEXT("NOT_AVAILABLE"));
292
- return true;
293
- #endif
294
- }
295
- // Handle create_niagara_system
296
- else if (LowerSub == TEXT("create_niagara_system")) {
297
- FString Name;
298
- LocalPayload->TryGetStringField(TEXT("name"), Name);
299
- FString Path;
300
- LocalPayload->TryGetStringField(TEXT("path"), Path);
301
-
302
- if (Name.IsEmpty() || Path.IsEmpty()) {
303
- SendAutomationError(RequestingSocket, RequestId,
304
- TEXT("name and path required"),
305
- TEXT("INVALID_ARGUMENT"));
306
- return true;
307
- }
308
-
309
- // Basic asset creation logic (requires UNiagaraSystemFactoryNew or
310
- // similar) Since we are inside EffectHandlers, usually we spawn things.
311
- // creation might belong in AssetHandlers But per plan, we implement it
312
- // here to unblock.
313
-
314
- UPackage *Package = CreatePackage(*Path);
315
- if (!Package) {
316
- SendAutomationError(RequestingSocket, RequestId,
317
- TEXT("Failed to create package"),
318
- TEXT("CREATE_FAILED"));
319
- return true;
320
- }
321
-
322
- UNiagaraSystemFactoryNew *Factory = NewObject<UNiagaraSystemFactoryNew>();
323
- UNiagaraSystem *NewSystem =
324
- Cast<UNiagaraSystem>(Factory->FactoryCreateNew(
325
- UNiagaraSystem::StaticClass(), Package, *Name,
326
- RF_Public | RF_Standalone, nullptr, nullptr));
327
-
328
- if (NewSystem) {
329
- FAssetRegistryModule::AssetCreated(NewSystem);
330
- Package->MarkPackageDirty();
331
- // Return validity check
332
- FString AssetPath = NewSystem->GetPathName();
333
- // If it's something like /Game/Path/Asset.Asset, try to simplify for
334
- // user convenience But LoadAsset works with the full Object path or
335
- // Package path. Let's ensure we save it properly.
336
- UEditorAssetLibrary::SaveAsset(AssetPath);
337
-
338
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
339
- Resp->SetBoolField(TEXT("success"), true);
340
- Resp->SetStringField(TEXT("assetPath"), AssetPath);
341
- Resp->SetStringField(TEXT("packageName"), Package->GetPathName());
342
-
343
- SendAutomationResponse(RequestingSocket, RequestId, true,
344
- TEXT("Niagara System created"), Resp);
345
- } else {
346
- SendAutomationError(RequestingSocket, RequestId,
347
- TEXT("Factory failed to create system"),
348
- TEXT("CREATE_FAILED"));
349
- }
350
- return true;
351
- }
352
-
353
- // Handle debug shapes
354
- if (LowerSub == TEXT("debug_shape")) {
355
- FString ShapeType;
356
- LocalPayload->TryGetStringField(TEXT("shapeType"), ShapeType);
357
- if (ShapeType.IsEmpty()) {
358
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
359
- Resp->SetBoolField(TEXT("success"), false);
360
- Resp->SetStringField(
361
- TEXT("error"),
362
- TEXT("shapeType parameter required for debug shape drawing"));
363
- SendAutomationResponse(RequestingSocket, RequestId, false,
364
- TEXT("shapeType required"), Resp,
365
- TEXT("INVALID_ARGUMENT"));
366
- return true;
367
- }
368
-
369
- // Location
370
- FVector Loc(0, 0, 0);
371
- if (LocalPayload->HasField(TEXT("location"))) {
372
- const TSharedPtr<FJsonValue> LocVal =
373
- LocalPayload->TryGetField(TEXT("location"));
374
- if (LocVal.IsValid()) {
375
- if (LocVal->Type == EJson::Array) {
376
- const TArray<TSharedPtr<FJsonValue>> &Arr = LocVal->AsArray();
377
- if (Arr.Num() >= 3)
378
- Loc =
379
- FVector((float)Arr[0]->AsNumber(), (float)Arr[1]->AsNumber(),
380
- (float)Arr[2]->AsNumber());
381
- } else if (LocVal->Type == EJson::Object) {
382
- const TSharedPtr<FJsonObject> O = LocVal->AsObject();
383
- if (O.IsValid())
384
- Loc = FVector(
385
- (float)(O->HasField(TEXT("x")) ? O->GetNumberField(TEXT("x"))
386
- : 0.0),
387
- (float)(O->HasField(TEXT("y")) ? O->GetNumberField(TEXT("y"))
388
- : 0.0),
389
- (float)(O->HasField(TEXT("z")) ? O->GetNumberField(TEXT("z"))
390
- : 0.0));
391
- }
392
- }
393
- }
394
-
395
- // Color (default: red)
396
- TArray<double> ColorArr = {255, 0, 0, 255};
397
- const TArray<TSharedPtr<FJsonValue>> *ColorJsonArr = nullptr;
398
- if (LocalPayload->TryGetArrayField(TEXT("color"), ColorJsonArr) &&
399
- ColorJsonArr && ColorJsonArr->Num() >= 4) {
400
- ColorArr[0] = (*ColorJsonArr)[0]->AsNumber();
401
- ColorArr[1] = (*ColorJsonArr)[1]->AsNumber();
402
- ColorArr[2] = (*ColorJsonArr)[2]->AsNumber();
403
- ColorArr[3] = (*ColorJsonArr)[3]->AsNumber();
404
- }
405
-
406
- // Duration (default: 5.0 seconds)
407
- const float Duration =
408
- LocalPayload->HasField(TEXT("duration"))
409
- ? (float)LocalPayload->GetNumberField(TEXT("duration"))
410
- : 5.0f;
411
-
412
- // Size/Radius (default: 100.0)
413
- const float Size = LocalPayload->HasField(TEXT("size"))
414
- ? (float)LocalPayload->GetNumberField(TEXT("size"))
415
- : 100.0f;
416
-
417
- // Thickness for lines (default: 2.0)
418
- const float Thickness =
419
- LocalPayload->HasField(TEXT("thickness"))
420
- ? (float)LocalPayload->GetNumberField(TEXT("thickness"))
421
- : 2.0f;
422
-
423
- #if WITH_EDITOR
424
- if (!GEditor) {
425
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
426
- Resp->SetBoolField(TEXT("success"), false);
427
- Resp->SetStringField(TEXT("error"),
428
- TEXT("Editor not available for debug drawing"));
429
- SendAutomationResponse(RequestingSocket, RequestId, false,
430
- TEXT("Editor not available"), Resp,
431
- TEXT("EDITOR_NOT_AVAILABLE"));
432
- return true;
433
- }
434
-
435
- // Get the current world for debug drawing
436
- UWorld *World = GEditor->GetEditorWorldContext().World();
437
- if (!World) {
438
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
439
- Resp->SetBoolField(TEXT("success"), false);
440
- Resp->SetStringField(TEXT("error"),
441
- TEXT("No world available for debug drawing"));
442
- SendAutomationResponse(RequestingSocket, RequestId, false,
443
- TEXT("No world available"), Resp,
444
- TEXT("NO_WORLD"));
445
- return true;
446
- }
447
-
448
- const FColor DebugColor((uint8)ColorArr[0], (uint8)ColorArr[1],
449
- (uint8)ColorArr[2], (uint8)ColorArr[3]);
450
- const FString LowerShapeType = ShapeType.ToLower();
451
-
452
- if (LowerShapeType == TEXT("sphere")) {
453
- DrawDebugSphere(World, Loc, Size, 16, DebugColor, false, Duration, 0,
454
- Thickness);
455
- } else if (LowerShapeType == TEXT("box")) {
456
- FVector BoxSize = FVector(Size);
457
- if (LocalPayload->HasField(TEXT("boxSize"))) {
458
- const TArray<TSharedPtr<FJsonValue>> *BoxSizeArr = nullptr;
459
- if (LocalPayload->TryGetArrayField(TEXT("boxSize"), BoxSizeArr) &&
460
- BoxSizeArr && BoxSizeArr->Num() >= 3) {
461
- BoxSize = FVector((float)(*BoxSizeArr)[0]->AsNumber(),
462
- (float)(*BoxSizeArr)[1]->AsNumber(),
463
- (float)(*BoxSizeArr)[2]->AsNumber());
464
- }
465
- }
466
- DrawDebugBox(World, Loc, BoxSize, FRotator::ZeroRotator.Quaternion(),
467
- DebugColor, false, Duration, 0, Thickness);
468
- } else if (LowerShapeType == TEXT("circle")) {
469
- DrawDebugCircle(World, Loc, Size, 32, DebugColor, false, Duration, 0,
470
- Thickness, FVector::UpVector);
471
- } else if (LowerShapeType == TEXT("line")) {
472
- FVector EndLoc = Loc + FVector(100, 0, 0);
473
- if (LocalPayload->HasField(TEXT("endLocation"))) {
474
- const TSharedPtr<FJsonValue> EndVal =
475
- LocalPayload->TryGetField(TEXT("endLocation"));
476
- if (EndVal.IsValid()) {
477
- if (EndVal->Type == EJson::Array) {
478
- const TArray<TSharedPtr<FJsonValue>> &Arr = EndVal->AsArray();
479
- if (Arr.Num() >= 3)
480
- EndLoc = FVector((float)Arr[0]->AsNumber(),
481
- (float)Arr[1]->AsNumber(),
482
- (float)Arr[2]->AsNumber());
483
- } else if (EndVal->Type == EJson::Object) {
484
- const TSharedPtr<FJsonObject> O = EndVal->AsObject();
485
- if (O.IsValid())
486
- EndLoc = FVector((float)(O->HasField(TEXT("x"))
487
- ? O->GetNumberField(TEXT("x"))
488
- : 0.0),
489
- (float)(O->HasField(TEXT("y"))
490
- ? O->GetNumberField(TEXT("y"))
491
- : 0.0),
492
- (float)(O->HasField(TEXT("z"))
493
- ? O->GetNumberField(TEXT("z"))
494
- : 0.0));
495
- }
496
- }
497
- }
498
- DrawDebugLine(World, Loc, EndLoc, DebugColor, false, Duration, 0,
499
- Thickness);
500
- } else if (LowerShapeType == TEXT("point")) {
501
- DrawDebugPoint(World, Loc, Size, DebugColor, false, Duration);
502
- } else if (LowerShapeType == TEXT("coordinate")) {
503
- FRotator Rot = FRotator::ZeroRotator;
504
- if (LocalPayload->HasField(TEXT("rotation"))) {
505
- const TArray<TSharedPtr<FJsonValue>> *RotArr = nullptr;
506
- if (LocalPayload->TryGetArrayField(TEXT("rotation"), RotArr) &&
507
- RotArr && RotArr->Num() >= 3) {
508
- Rot = FRotator((float)(*RotArr)[0]->AsNumber(),
509
- (float)(*RotArr)[1]->AsNumber(),
510
- (float)(*RotArr)[2]->AsNumber());
511
- }
512
- }
513
- DrawDebugCoordinateSystem(World, Loc, Rot, Size, false, Duration, 0,
514
- Thickness);
515
- } else if (LowerShapeType == TEXT("cylinder")) {
516
- FVector EndLoc = Loc + FVector(0, 0, 100);
517
- if (LocalPayload->HasField(TEXT("endLocation"))) {
518
- const TSharedPtr<FJsonValue> EndVal =
519
- LocalPayload->TryGetField(TEXT("endLocation"));
520
- if (EndVal.IsValid()) {
521
- if (EndVal->Type == EJson::Array) {
522
- const TArray<TSharedPtr<FJsonValue>> &Arr = EndVal->AsArray();
523
- if (Arr.Num() >= 3)
524
- EndLoc = FVector((float)Arr[0]->AsNumber(),
525
- (float)Arr[1]->AsNumber(),
526
- (float)Arr[2]->AsNumber());
527
- } else if (EndVal->Type == EJson::Object) {
528
- const TSharedPtr<FJsonObject> O = EndVal->AsObject();
529
- if (O.IsValid())
530
- EndLoc = FVector((float)(O->HasField(TEXT("x"))
531
- ? O->GetNumberField(TEXT("x"))
532
- : 0.0),
533
- (float)(O->HasField(TEXT("y"))
534
- ? O->GetNumberField(TEXT("y"))
535
- : 0.0),
536
- (float)(O->HasField(TEXT("z"))
537
- ? O->GetNumberField(TEXT("z"))
538
- : 0.0));
539
- }
540
- }
541
- }
542
- DrawDebugCylinder(World, Loc, EndLoc, Size, 16, DebugColor, false,
543
- Duration, 0, Thickness);
544
- } else if (LowerShapeType == TEXT("cone")) {
545
- FVector Direction = FVector::UpVector;
546
- if (LocalPayload->HasField(TEXT("direction"))) {
547
- const TSharedPtr<FJsonValue> DirVal =
548
- LocalPayload->TryGetField(TEXT("direction"));
549
- if (DirVal.IsValid()) {
550
- if (DirVal->Type == EJson::Array) {
551
- const TArray<TSharedPtr<FJsonValue>> &Arr = DirVal->AsArray();
552
- if (Arr.Num() >= 3)
553
- Direction = FVector((float)Arr[0]->AsNumber(),
554
- (float)Arr[1]->AsNumber(),
555
- (float)Arr[2]->AsNumber());
556
- } else if (DirVal->Type == EJson::Object) {
557
- const TSharedPtr<FJsonObject> O = DirVal->AsObject();
558
- if (O.IsValid())
559
- Direction = FVector((float)(O->HasField(TEXT("x"))
560
- ? O->GetNumberField(TEXT("x"))
561
- : 0.0),
562
- (float)(O->HasField(TEXT("y"))
563
- ? O->GetNumberField(TEXT("y"))
564
- : 0.0),
565
- (float)(O->HasField(TEXT("z"))
566
- ? O->GetNumberField(TEXT("z"))
567
- : 0.0));
568
- }
569
- }
570
- }
571
- float Length = 100.0f;
572
- if (LocalPayload->HasField(TEXT("length"))) {
573
- Length = (float)LocalPayload->GetNumberField(TEXT("length"));
574
- }
575
- // Default to a 45 degree cone if not specified
576
- float AngleWidth = FMath::DegreesToRadians(45.0f);
577
- float AngleHeight = FMath::DegreesToRadians(45.0f);
578
-
579
- if (LocalPayload->HasField(TEXT("angle"))) {
580
- float AngleDeg = (float)LocalPayload->GetNumberField(TEXT("angle"));
581
- AngleWidth = AngleHeight = FMath::DegreesToRadians(AngleDeg);
582
- }
583
-
584
- DrawDebugCone(World, Loc, Direction, Length, AngleWidth, AngleHeight,
585
- 16, DebugColor, false, Duration, 0, Thickness);
586
- } else if (LowerShapeType == TEXT("capsule")) {
587
- FQuat Rot = FQuat::Identity;
588
- if (LocalPayload->HasField(TEXT("rotation"))) {
589
- const TArray<TSharedPtr<FJsonValue>> *RotArr = nullptr;
590
- if (LocalPayload->TryGetArrayField(TEXT("rotation"), RotArr) &&
591
- RotArr && RotArr->Num() >= 3) {
592
- Rot = FRotator((float)(*RotArr)[0]->AsNumber(),
593
- (float)(*RotArr)[1]->AsNumber(),
594
- (float)(*RotArr)[2]->AsNumber())
595
- .Quaternion();
596
- }
597
- }
598
- float HalfHeight = Size; // Default if not specified
599
- if (LocalPayload->HasField(TEXT("halfHeight"))) {
600
- HalfHeight = (float)LocalPayload->GetNumberField(TEXT("halfHeight"));
601
- }
602
- DrawDebugCapsule(World, Loc, HalfHeight, Size, Rot, DebugColor, false,
603
- Duration, 0, Thickness);
604
- } else if (LowerShapeType == TEXT("arrow")) {
605
- FVector EndLoc = Loc + FVector(100, 0, 0);
606
- if (LocalPayload->HasField(TEXT("endLocation"))) {
607
- // ... parsing logic same as line ...
608
- const TSharedPtr<FJsonValue> EndVal =
609
- LocalPayload->TryGetField(TEXT("endLocation"));
610
- if (EndVal.IsValid()) {
611
- if (EndVal->Type == EJson::Array) {
612
- const TArray<TSharedPtr<FJsonValue>> &Arr = EndVal->AsArray();
613
- if (Arr.Num() >= 3)
614
- EndLoc = FVector((float)Arr[0]->AsNumber(),
615
- (float)Arr[1]->AsNumber(),
616
- (float)Arr[2]->AsNumber());
617
- } else if (EndVal->Type == EJson::Object) {
618
- const TSharedPtr<FJsonObject> O = EndVal->AsObject();
619
- if (O.IsValid())
620
- EndLoc = FVector((float)(O->HasField(TEXT("x"))
621
- ? O->GetNumberField(TEXT("x"))
622
- : 0.0),
623
- (float)(O->HasField(TEXT("y"))
624
- ? O->GetNumberField(TEXT("y"))
625
- : 0.0),
626
- (float)(O->HasField(TEXT("z"))
627
- ? O->GetNumberField(TEXT("z"))
628
- : 0.0));
629
- }
630
- }
631
- }
632
- float ArrowSize = Size > 0 ? Size : 10.0f;
633
- DrawDebugDirectionalArrow(World, Loc, EndLoc, ArrowSize, DebugColor,
634
- false, Duration, 0, Thickness);
635
- } else if (LowerShapeType == TEXT("plane")) {
636
- // Draw a simple plane using a box with 0 height or DrawDebugSolidPlane
637
- // if available but DrawDebugBox is safer for wireframe Using Box with
638
- // minimal Z thickness
639
- FVector BoxSize = FVector(Size, Size, 1.0f);
640
- if (LocalPayload->HasField(TEXT("boxSize"))) {
641
- // ... parsing ...
642
- }
643
- FQuat Rot = FQuat::Identity;
644
- if (LocalPayload->HasField(TEXT("rotation"))) {
645
- const TArray<TSharedPtr<FJsonValue>> *RotArr = nullptr;
646
- if (LocalPayload->TryGetArrayField(TEXT("rotation"), RotArr) &&
647
- RotArr && RotArr->Num() >= 3) {
648
- Rot = FRotator((float)(*RotArr)[0]->AsNumber(),
649
- (float)(*RotArr)[1]->AsNumber(),
650
- (float)(*RotArr)[2]->AsNumber())
651
- .Quaternion();
652
- }
653
- }
654
- DrawDebugBox(World, Loc, BoxSize, Rot, DebugColor, false, Duration, 0,
655
- Thickness);
656
- } else {
657
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
658
- Resp->SetBoolField(TEXT("success"), false);
659
- Resp->SetStringField(
660
- TEXT("error"),
661
- FString::Printf(TEXT("Unsupported shape type: %s"), *ShapeType));
662
- Resp->SetStringField(
663
- TEXT("supportedShapes"),
664
- TEXT("sphere, box, circle, line, point, coordinate, cylinder, "
665
- "cone, capsule, arrow, plane"));
666
- SendAutomationResponse(RequestingSocket, RequestId, false,
667
- TEXT("Unsupported shape type"), Resp,
668
- TEXT("UNSUPPORTED_SHAPE"));
669
- return true;
670
- }
671
-
672
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
673
- Resp->SetBoolField(TEXT("success"), true);
674
- Resp->SetStringField(TEXT("shapeType"), ShapeType);
675
- Resp->SetStringField(
676
- TEXT("location"),
677
- FString::Printf(TEXT("%.2f,%.2f,%.2f"), Loc.X, Loc.Y, Loc.Z));
678
- Resp->SetNumberField(TEXT("duration"), Duration);
679
- SendAutomationResponse(RequestingSocket, RequestId, true,
680
- TEXT("Debug shape drawn"), Resp, FString());
681
- return true;
682
- #else
683
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
684
- Resp->SetBoolField(TEXT("success"), false);
685
- Resp->SetStringField(TEXT("error"),
686
- TEXT("Debug shape drawing requires editor build"));
687
- Resp->SetStringField(TEXT("shapeType"), ShapeType);
688
- SendAutomationResponse(
689
- RequestingSocket, RequestId, false,
690
- TEXT("Debug shape drawing not available in non-editor build"), Resp,
691
- TEXT("NOT_AVAILABLE"));
692
- return true;
693
- #endif
694
- }
695
-
696
- // Handle niagara sub-action (delegates to existing spawn_niagara logic)
697
- if (LowerSub == TEXT("niagara") || LowerSub == TEXT("spawn_niagara")) {
698
- // Reuse logic below
699
- } else if (LowerSub.Equals(TEXT("set_niagara_parameter"))) {
700
- FString SystemName;
701
- LocalPayload->TryGetStringField(TEXT("systemName"), SystemName);
702
- FString ParameterName;
703
- LocalPayload->TryGetStringField(TEXT("parameterName"), ParameterName);
704
- FString ParameterType;
705
- LocalPayload->TryGetStringField(TEXT("parameterType"), ParameterType);
706
- if (ParameterName.IsEmpty()) {
707
- SendAutomationResponse(RequestingSocket, RequestId, false,
708
- TEXT("parameterName required"), nullptr,
709
- TEXT("INVALID_ARGUMENT"));
710
- return true;
711
- }
712
- if (ParameterType.IsEmpty())
713
- ParameterType = TEXT("Float");
714
-
715
- UE_LOG(
716
- LogMcpAutomationBridgeSubsystem, Verbose,
717
- TEXT("SetNiagaraParameter: Looking for actor '%s' to set param '%s'"),
718
- *SystemName, *ParameterName);
719
-
720
- #if WITH_EDITOR
721
- if (!GEditor) {
722
- SendAutomationResponse(RequestingSocket, RequestId, false,
723
- TEXT("Editor not available"), nullptr,
724
- TEXT("EDITOR_NOT_AVAILABLE"));
725
- return true;
726
- }
727
- UEditorActorSubsystem *ActorSS =
728
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
729
- if (!ActorSS) {
730
- SendAutomationResponse(RequestingSocket, RequestId, false,
731
- TEXT("EditorActorSubsystem not available"),
732
- nullptr, TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
733
- return true;
734
- }
735
-
736
- const FName ParamName(*ParameterName);
737
- const TSharedPtr<FJsonValue> ValueField =
738
- LocalPayload->TryGetField(TEXT("value"));
739
-
740
- TArray<AActor *> AllActors = ActorSS->GetAllLevelActors();
741
- bool bApplied = false;
742
-
743
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
744
- TEXT("SetNiagaraParameter: Looking for actor '%s'"), *SystemName);
745
-
746
- bool bActorFound = false;
747
- bool bComponentFound = false;
748
-
749
- for (AActor *Actor : AllActors) {
750
- if (!Actor)
751
- continue;
752
- if (!Actor->GetActorLabel().Equals(SystemName, ESearchCase::IgnoreCase))
753
- continue;
754
-
755
- bActorFound = true;
756
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
757
- TEXT("SetNiagaraParameter: Found actor '%s'"), *SystemName);
758
- UNiagaraComponent *NiComp =
759
- Actor->FindComponentByClass<UNiagaraComponent>();
760
- if (!NiComp) {
761
- UE_LOG(
762
- LogMcpAutomationBridgeSubsystem, Warning,
763
- TEXT("SetNiagaraParameter: Actor '%s' has no NiagaraComponent"),
764
- *SystemName);
765
- // Keep looking? No, actor label is unique-ish. But let's
766
- // assume unique.
767
- // But maybe we should break if we found the actor but no component?
768
- bComponentFound = false;
769
- break;
770
- }
771
- bComponentFound = true;
772
-
773
- if (ParameterType.Equals(TEXT("Float"), ESearchCase::IgnoreCase)) {
774
- double NumberValue = 0.0;
775
- bool bHasNumber =
776
- LocalPayload->TryGetNumberField(TEXT("value"), NumberValue);
777
- if (!bHasNumber && ValueField.IsValid()) {
778
- if (ValueField->Type == EJson::Number) {
779
- NumberValue = ValueField->AsNumber();
780
- bHasNumber = true;
781
- } else if (ValueField->Type == EJson::Object) {
782
- const TSharedPtr<FJsonObject> Obj = ValueField->AsObject();
783
- if (Obj.IsValid())
784
- bHasNumber = Obj->TryGetNumberField(TEXT("v"), NumberValue);
785
- }
786
- }
787
- if (bHasNumber) {
788
- NiComp->SetVariableFloat(ParamName,
789
- static_cast<float>(NumberValue));
790
- bApplied = true;
791
- }
792
- } else if (ParameterType.Equals(TEXT("Vector"),
793
- ESearchCase::IgnoreCase)) {
794
- const TSharedPtr<FJsonValue> Val =
795
- LocalPayload->TryGetField(TEXT("value"));
796
- UE_LOG(
797
- LogMcpAutomationBridgeSubsystem, Display,
798
- TEXT("SetNiagaraParameter: DEBUG - Processing Vector for '%s'"),
799
- *ParamName.ToString());
800
-
801
- const TArray<TSharedPtr<FJsonValue>> *ArrValue = nullptr;
802
- const TSharedPtr<FJsonObject> *ObjValue = nullptr;
803
- if (LocalPayload->TryGetArrayField(TEXT("value"), ArrValue) &&
804
- ArrValue && ArrValue->Num() >= 3) {
805
- const float X = static_cast<float>((*ArrValue)[0]->AsNumber());
806
- const float Y = static_cast<float>((*ArrValue)[1]->AsNumber());
807
- const float Z = static_cast<float>((*ArrValue)[2]->AsNumber());
808
- NiComp->SetVariableVec3(ParamName, FVector(X, Y, Z));
809
- bApplied = true;
810
- UE_LOG(LogMcpAutomationBridgeSubsystem, Display,
811
- TEXT("SetNiagaraParameter: DEBUG - Applied Vector from "
812
- "Array: %f, %f, %f"),
813
- X, Y, Z);
814
- } else if (LocalPayload->TryGetObjectField(TEXT("value"), ObjValue) &&
815
- ObjValue) {
816
- double VX = 0, VY = 0, VZ = 0;
817
- (*ObjValue)->TryGetNumberField(TEXT("x"), VX);
818
- (*ObjValue)->TryGetNumberField(TEXT("y"), VY);
819
- (*ObjValue)->TryGetNumberField(TEXT("z"), VZ);
820
- NiComp->SetVariableVec3(ParamName,
821
- FVector((float)VX, (float)VY, (float)VZ));
822
- bApplied = true;
823
- UE_LOG(LogMcpAutomationBridgeSubsystem, Display,
824
- TEXT("SetNiagaraParameter: DEBUG - Applied Vector from "
825
- "Object: %f, %f, %f"),
826
- VX, VY, VZ);
827
- } else {
828
- UE_LOG(LogMcpAutomationBridgeSubsystem, Warning,
829
- TEXT("SetNiagaraParameter: DEBUG - Failed to parse Vector "
830
- "value."));
831
- }
832
- } else if (ParameterType.Equals(TEXT("Color"),
833
- ESearchCase::IgnoreCase)) {
834
- const TArray<TSharedPtr<FJsonValue>> *ArrValue = nullptr;
835
- if (LocalPayload->TryGetArrayField(TEXT("value"), ArrValue) &&
836
- ArrValue && ArrValue->Num() >= 3) {
837
- const float R = static_cast<float>((*ArrValue)[0]->AsNumber());
838
- const float G = static_cast<float>((*ArrValue)[1]->AsNumber());
839
- const float B = static_cast<float>((*ArrValue)[2]->AsNumber());
840
- const float Alpha =
841
- ArrValue->Num() > 3
842
- ? static_cast<float>((*ArrValue)[3]->AsNumber())
843
- : 1.0f;
844
- NiComp->SetVariableLinearColor(ParamName,
845
- FLinearColor(R, G, B, Alpha));
846
- bApplied = true;
847
- }
848
- } else if (ParameterType.Equals(TEXT("Bool"),
849
- ESearchCase::IgnoreCase)) {
850
- bool bValue = false;
851
- bool bHasBool = LocalPayload->TryGetBoolField(TEXT("value"), bValue);
852
- if (bHasBool) {
853
- NiComp->SetVariableBool(ParamName, bValue);
854
- bApplied = true;
855
- }
856
- }
857
-
858
- // If we found the actor and component but failed to apply, we stop
859
- // searching.
860
- break;
861
- }
862
-
863
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
864
- Resp->SetBoolField(TEXT("success"), bApplied);
865
- Resp->SetBoolField(TEXT("applied"), bApplied);
866
- Resp->SetStringField(TEXT("actorName"), SystemName);
867
- Resp->SetStringField(TEXT("parameterName"), ParameterName);
868
- Resp->SetStringField(TEXT("parameterType"), ParameterType);
869
-
870
- if (bApplied) {
871
- SendAutomationResponse(RequestingSocket, RequestId, true,
872
- TEXT("Niagara parameter set"), Resp, FString());
873
- } else {
874
- FString ErrMsg = TEXT("Niagara parameter not applied");
875
- FString ErrCode = TEXT("SET_NIAGARA_PARAM_FAILED");
876
-
877
- if (!bActorFound) {
878
- ErrMsg = FString::Printf(TEXT("Actor '%s' not found"), *SystemName);
879
- ErrCode = TEXT("ACTOR_NOT_FOUND");
880
- } else if (!bComponentFound) {
881
- ErrMsg = FString::Printf(TEXT("Actor '%s' has no Niagara component"),
882
- *SystemName);
883
- ErrCode = TEXT("COMPONENT_NOT_FOUND");
884
- } else {
885
- // Check common failure reasons
886
- // Invalid Type?
887
- if (!ParameterType.Equals(TEXT("Float"), ESearchCase::IgnoreCase) &&
888
- !ParameterType.Equals(TEXT("Vector"), ESearchCase::IgnoreCase) &&
889
- !ParameterType.Equals(TEXT("Color"), ESearchCase::IgnoreCase) &&
890
- !ParameterType.Equals(TEXT("Bool"), ESearchCase::IgnoreCase)) {
891
- ErrMsg = FString::Printf(TEXT("Invalid parameter type: %s"),
892
- *ParameterType);
893
- ErrCode = TEXT("INVALID_ARGUMENT");
894
- }
895
- }
896
-
897
- SendAutomationResponse(RequestingSocket, RequestId, false, ErrMsg, Resp,
898
- ErrCode);
899
- }
900
- return true;
901
- #else
902
- SendAutomationResponse(
903
- RequestingSocket, RequestId, false,
904
- TEXT("set_niagara_parameter requires editor build."), nullptr,
905
- TEXT("NOT_IMPLEMENTED"));
906
- return true;
907
- #endif
908
- } else if (LowerSub.Equals(TEXT("activate_niagara"))) {
909
- FString SystemName;
910
- LocalPayload->TryGetStringField(TEXT("systemName"), SystemName);
911
- bool bReset = LocalPayload->HasField(TEXT("reset"))
912
- ? LocalPayload->GetBoolField(TEXT("reset"))
913
- : true;
914
-
915
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
916
- TEXT("ActivateNiagara: Looking for actor '%s'"), *SystemName);
917
-
918
- #if WITH_EDITOR
919
- UEditorActorSubsystem *ActorSS =
920
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
921
- TArray<AActor *> AllActors = ActorSS->GetAllLevelActors();
922
- bool bFound = false;
923
- for (AActor *Actor : AllActors) {
924
- if (!Actor)
925
- continue;
926
- if (!Actor->GetActorLabel().Equals(SystemName, ESearchCase::IgnoreCase))
927
- continue;
928
-
929
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
930
- TEXT("ActivateNiagara: Found actor '%s'"), *SystemName);
931
- UNiagaraComponent *NiComp =
932
- Actor->FindComponentByClass<UNiagaraComponent>();
933
- if (!NiComp)
934
- continue;
935
-
936
- NiComp->Activate(bReset);
937
- bFound = true;
938
- break;
939
- }
940
- if (bFound) {
941
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
942
- Resp->SetBoolField(TEXT("success"), true);
943
- Resp->SetStringField(TEXT("actorName"), SystemName);
944
- Resp->SetBoolField(TEXT("active"), true);
945
- SendAutomationResponse(RequestingSocket, RequestId, true,
946
- TEXT("Niagara system activated."), Resp);
947
- } else
948
- SendAutomationResponse(RequestingSocket, RequestId, false,
949
- TEXT("Niagara system not found."), nullptr,
950
- TEXT("SYSTEM_NOT_FOUND"));
951
- return true;
952
- #else
953
- SendAutomationResponse(RequestingSocket, RequestId, false,
954
- TEXT("activate_niagara requires editor build."),
955
- nullptr, TEXT("NOT_IMPLEMENTED"));
956
- return true;
957
- #endif
958
- } else if (LowerSub.Equals(TEXT("deactivate_niagara"))) {
959
- FString SystemName;
960
- LocalPayload->TryGetStringField(TEXT("systemName"), SystemName);
961
- if (SystemName.IsEmpty())
962
- LocalPayload->TryGetStringField(TEXT("actorName"), SystemName);
963
-
964
- #if WITH_EDITOR
965
- UEditorActorSubsystem *ActorSS =
966
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
967
- TArray<AActor *> AllActors = ActorSS->GetAllLevelActors();
968
- bool bFound = false;
969
- for (AActor *Actor : AllActors) {
970
- if (!Actor)
971
- continue;
972
- if (!Actor->GetActorLabel().Equals(SystemName, ESearchCase::IgnoreCase))
973
- continue;
974
-
975
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
976
- TEXT("DeactivateNiagara: Found actor '%s'"), *SystemName);
977
- UNiagaraComponent *NiComp =
978
- Actor->FindComponentByClass<UNiagaraComponent>();
979
- if (!NiComp)
980
- continue;
981
-
982
- NiComp->Deactivate();
983
- bFound = true;
984
- break;
985
- }
986
- if (bFound) {
987
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
988
- Resp->SetBoolField(TEXT("success"), true);
989
- Resp->SetStringField(TEXT("actorName"), SystemName);
990
- Resp->SetBoolField(TEXT("active"), false);
991
- SendAutomationResponse(RequestingSocket, RequestId, true,
992
- TEXT("Niagara system deactivated."), Resp);
993
- } else
994
- SendAutomationResponse(RequestingSocket, RequestId, false,
995
- TEXT("Niagara system not found."), nullptr,
996
- TEXT("SYSTEM_NOT_FOUND"));
997
- return true;
998
- #else
999
- SendAutomationResponse(RequestingSocket, RequestId, false,
1000
- TEXT("deactivate_niagara requires editor build."),
1001
- nullptr, TEXT("NOT_IMPLEMENTED"));
1002
- return true;
1003
- #endif
1004
- } else if (LowerSub.Equals(TEXT("advance_simulation"))) {
1005
- FString SystemName;
1006
- LocalPayload->TryGetStringField(TEXT("systemName"), SystemName);
1007
- if (SystemName.IsEmpty())
1008
- LocalPayload->TryGetStringField(TEXT("actorName"), SystemName);
1009
-
1010
- double DeltaTime = 0.1;
1011
- LocalPayload->TryGetNumberField(TEXT("deltaTime"), DeltaTime);
1012
- int32 Steps = 1;
1013
- LocalPayload->TryGetNumberField(TEXT("steps"), Steps);
1014
-
1015
- #if WITH_EDITOR
1016
- UEditorActorSubsystem *ActorSS =
1017
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1018
- TArray<AActor *> AllActors = ActorSS->GetAllLevelActors();
1019
- bool bFound = false;
1020
- for (AActor *Actor : AllActors) {
1021
- if (!Actor)
1022
- continue;
1023
- if (!Actor->GetActorLabel().Equals(SystemName, ESearchCase::IgnoreCase))
1024
- continue;
1025
-
1026
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
1027
- TEXT("AdvanceSimulation: Found actor '%s'"), *SystemName);
1028
- UNiagaraComponent *NiComp =
1029
- Actor->FindComponentByClass<UNiagaraComponent>();
1030
- if (!NiComp)
1031
- continue;
1032
-
1033
- for (int i = 0; i < Steps; i++) {
1034
- NiComp->AdvanceSimulation(Steps, DeltaTime);
1035
- }
1036
- bFound = true;
1037
- break;
1038
- }
1039
- if (bFound) {
1040
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1041
- Resp->SetBoolField(TEXT("success"), true);
1042
- Resp->SetStringField(TEXT("actorName"), SystemName);
1043
- Resp->SetNumberField(TEXT("steps"), Steps);
1044
- SendAutomationResponse(RequestingSocket, RequestId, true,
1045
- TEXT("Niagara simulation advanced."), Resp);
1046
- } else
1047
- SendAutomationResponse(RequestingSocket, RequestId, false,
1048
- TEXT("Niagara system not found."), nullptr,
1049
- TEXT("SYSTEM_NOT_FOUND"));
1050
- return true;
1051
- #else
1052
- SendAutomationResponse(RequestingSocket, RequestId, false,
1053
- TEXT("advance_simulation requires editor build."),
1054
- nullptr, TEXT("NOT_IMPLEMENTED"));
1055
- return true;
1056
- #endif
1057
- } else if (LowerSub.Equals(TEXT("create_dynamic_light"))) {
1058
- FString LightName;
1059
- LocalPayload->TryGetStringField(TEXT("lightName"), LightName);
1060
- FString LightType;
1061
- LocalPayload->TryGetStringField(TEXT("lightType"), LightType);
1062
- if (LightType.IsEmpty())
1063
- LightType = TEXT("Point");
1064
-
1065
- // location
1066
- FVector Loc(0, 0, 0);
1067
- if (LocalPayload->HasField(TEXT("location"))) {
1068
- const TSharedPtr<FJsonValue> LocVal =
1069
- LocalPayload->TryGetField(TEXT("location"));
1070
- if (LocVal.IsValid()) {
1071
- if (LocVal->Type == EJson::Array) {
1072
- const TArray<TSharedPtr<FJsonValue>> &Arr = LocVal->AsArray();
1073
- if (Arr.Num() >= 3)
1074
- Loc =
1075
- FVector((float)Arr[0]->AsNumber(), (float)Arr[1]->AsNumber(),
1076
- (float)Arr[2]->AsNumber());
1077
- } else if (LocVal->Type == EJson::Object) {
1078
- const TSharedPtr<FJsonObject> O = LocVal->AsObject();
1079
- if (O.IsValid())
1080
- Loc = FVector(
1081
- (float)(O->HasField(TEXT("x")) ? O->GetNumberField(TEXT("x"))
1082
- : 0.0),
1083
- (float)(O->HasField(TEXT("y")) ? O->GetNumberField(TEXT("y"))
1084
- : 0.0),
1085
- (float)(O->HasField(TEXT("z")) ? O->GetNumberField(TEXT("z"))
1086
- : 0.0));
1087
- }
1088
- }
1089
- }
1090
-
1091
- double Intensity = 0.0;
1092
- LocalPayload->TryGetNumberField(TEXT("intensity"), Intensity);
1093
- // color can be array or object
1094
- bool bHasColor = false;
1095
- double Cr = 1.0, Cg = 1.0, Cb = 1.0, Ca = 1.0;
1096
- if (LocalPayload->HasField(TEXT("color"))) {
1097
- const TArray<TSharedPtr<FJsonValue>> *ColArr = nullptr;
1098
- if (LocalPayload->TryGetArrayField(TEXT("color"), ColArr) && ColArr &&
1099
- ColArr->Num() >= 3) {
1100
- bHasColor = true;
1101
- Cr = (*ColArr)[0]->AsNumber();
1102
- Cg = (*ColArr)[1]->AsNumber();
1103
- Cb = (*ColArr)[2]->AsNumber();
1104
- Ca = (ColArr->Num() > 3) ? (*ColArr)[3]->AsNumber() : 1.0;
1105
- } else {
1106
- const TSharedPtr<FJsonObject> *CO = nullptr;
1107
- if (LocalPayload->TryGetObjectField(TEXT("color"), CO) && CO &&
1108
- (*CO).IsValid()) {
1109
- bHasColor = true;
1110
- Cr = (*CO)->HasField(TEXT("r")) ? (*CO)->GetNumberField(TEXT("r"))
1111
- : Cr;
1112
- Cg = (*CO)->HasField(TEXT("g")) ? (*CO)->GetNumberField(TEXT("g"))
1113
- : Cg;
1114
- Cb = (*CO)->HasField(TEXT("b")) ? (*CO)->GetNumberField(TEXT("b"))
1115
- : Cb;
1116
- Ca = (*CO)->HasField(TEXT("a")) ? (*CO)->GetNumberField(TEXT("a"))
1117
- : Ca;
1118
- }
1119
- }
1120
- }
1121
-
1122
- // pulse param optional
1123
- bool bPulseEnabled = false;
1124
- double PulseFreq = 1.0;
1125
- if (LocalPayload->HasField(TEXT("pulse"))) {
1126
- const TSharedPtr<FJsonObject> *P = nullptr;
1127
- if (LocalPayload->TryGetObjectField(TEXT("pulse"), P) && P &&
1128
- (*P).IsValid()) {
1129
- (*P)->TryGetBoolField(TEXT("enabled"), bPulseEnabled);
1130
- (*P)->TryGetNumberField(TEXT("frequency"), PulseFreq);
1131
- }
1132
- }
1133
-
1134
- #if WITH_EDITOR
1135
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1136
- if (!GEditor) {
1137
- SendAutomationResponse(RequestingSocket, RequestId, false,
1138
- TEXT("Editor not available"), nullptr,
1139
- TEXT("EDITOR_NOT_AVAILABLE"));
1140
- return true;
1141
- }
1142
- UEditorActorSubsystem *ActorSS =
1143
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1144
- if (!ActorSS) {
1145
- SendAutomationResponse(RequestingSocket, RequestId, false,
1146
- TEXT("EditorActorSubsystem not available"),
1147
- nullptr, TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
1148
- return true;
1149
- }
1150
-
1151
- UClass *ChosenClass = APointLight::StaticClass();
1152
- UClass *CompClass = UPointLightComponent::StaticClass();
1153
- FString LT = LightType.ToLower();
1154
- if (LT == TEXT("spot") || LT == TEXT("spotlight")) {
1155
- ChosenClass = ASpotLight::StaticClass();
1156
- CompClass = USpotLightComponent::StaticClass();
1157
- } else if (LT == TEXT("directional") || LT == TEXT("directionallight")) {
1158
- ChosenClass = ADirectionalLight::StaticClass();
1159
- CompClass = UDirectionalLightComponent::StaticClass();
1160
- } else if (LT == TEXT("rect") || LT == TEXT("rectlight")) {
1161
- ChosenClass = ARectLight::StaticClass();
1162
- CompClass = URectLightComponent::StaticClass();
1163
- }
1164
-
1165
- AActor *Spawned = SpawnActorInActiveWorld<AActor>(ChosenClass, Loc,
1166
- FRotator::ZeroRotator);
1167
- if (!Spawned) {
1168
- SendAutomationResponse(RequestingSocket, RequestId, false,
1169
- TEXT("Failed to spawn light actor"), nullptr,
1170
- TEXT("CREATE_DYNAMIC_LIGHT_FAILED"));
1171
- return true;
1172
- }
1173
-
1174
- UActorComponent *C = Spawned->GetComponentByClass(CompClass);
1175
- if (C) {
1176
- if (ULightComponent *LC = Cast<ULightComponent>(C)) {
1177
- LC->SetIntensity(static_cast<float>(Intensity));
1178
- if (bHasColor) {
1179
- LC->SetLightColor(
1180
- FLinearColor(static_cast<float>(Cr), static_cast<float>(Cg),
1181
- static_cast<float>(Cb), static_cast<float>(Ca)));
1182
- }
1183
- }
1184
- }
1185
-
1186
- if (!LightName.IsEmpty()) {
1187
- Spawned->SetActorLabel(LightName);
1188
- }
1189
- if (bPulseEnabled) {
1190
- Spawned->Tags.Add(
1191
- FName(*FString::Printf(TEXT("MCP_PULSE:%g"), PulseFreq)));
1192
- }
1193
-
1194
- Resp->SetBoolField(TEXT("success"), true);
1195
- Resp->SetStringField(TEXT("actor"), Spawned->GetActorLabel());
1196
- SendAutomationResponse(RequestingSocket, RequestId, true,
1197
- TEXT("Dynamic light created"), Resp, FString());
1198
- return true;
1199
- #else
1200
- SendAutomationResponse(
1201
- RequestingSocket, RequestId, false,
1202
- TEXT("create_dynamic_light requires editor build."), nullptr,
1203
- TEXT("NOT_IMPLEMENTED"));
1204
- return true;
1205
- #endif
1206
- } else if (LowerSub.Equals(TEXT("cleanup"))) {
1207
- FString Filter;
1208
- LocalPayload->TryGetStringField(TEXT("filter"), Filter);
1209
- if (Filter.IsEmpty()) {
1210
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1211
- Resp->SetNumberField(TEXT("removed"), 0);
1212
- SendAutomationResponse(RequestingSocket, RequestId, true,
1213
- TEXT("Cleanup skipped (empty filter)"), Resp,
1214
- FString());
1215
- return true;
1216
- }
1217
- #if WITH_EDITOR
1218
- if (!GEditor) {
1219
- SendAutomationResponse(RequestingSocket, RequestId, false,
1220
- TEXT("Editor not available"), nullptr,
1221
- TEXT("EDITOR_NOT_AVAILABLE"));
1222
- return true;
1223
- }
1224
- UEditorActorSubsystem *ActorSS =
1225
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1226
- if (!ActorSS) {
1227
- SendAutomationResponse(RequestingSocket, RequestId, false,
1228
- TEXT("EditorActorSubsystem not available"),
1229
- nullptr, TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
1230
- return true;
1231
- }
1232
- TArray<AActor *> Actors = ActorSS->GetAllLevelActors();
1233
- TArray<FString> Removed;
1234
- for (AActor *A : Actors) {
1235
- if (!A)
1236
- continue;
1237
- FString Label = A->GetActorLabel();
1238
- if (Label.IsEmpty())
1239
- continue;
1240
- if (!Label.StartsWith(Filter, ESearchCase::IgnoreCase))
1241
- continue;
1242
- bool bDel = ActorSS->DestroyActor(A);
1243
- if (bDel)
1244
- Removed.Add(Label);
1245
- }
1246
- TArray<TSharedPtr<FJsonValue>> Arr;
1247
- for (const FString &S : Removed)
1248
- Arr.Add(MakeShared<FJsonValueString>(S));
1249
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1250
- Resp->SetArrayField(TEXT("removedActors"), Arr);
1251
- Resp->SetNumberField(TEXT("removed"), Removed.Num());
1252
- SendAutomationResponse(
1253
- RequestingSocket, RequestId, true,
1254
- FString::Printf(TEXT("Cleanup completed (removed=%d)"),
1255
- Removed.Num()),
1256
- Resp, FString());
1257
- return true;
1258
- #else
1259
- SendAutomationResponse(RequestingSocket, RequestId, false,
1260
- TEXT("cleanup requires editor build."), nullptr,
1261
- TEXT("NOT_IMPLEMENTED"));
1262
- return true;
1263
- #endif
1264
- }
1265
- }
1266
-
1267
- // Spawn Niagara system in-level as a NiagaraActor (editor-only)
1268
- bool bSpawnNiagara = Lower.Equals(TEXT("spawn_niagara"));
1269
- if (bIsCreateEffect) {
1270
- FString Sub;
1271
- LocalPayload->TryGetStringField(TEXT("action"), Sub);
1272
- FString LowerSub = Sub.ToLower();
1273
- if (LowerSub == TEXT("niagara") || LowerSub == TEXT("spawn_niagara"))
1274
- bSpawnNiagara = true;
1275
- // If SubAction is empty and Action is create_effect, we fallthrough to
1276
- // legacy behavior below
1277
- }
1278
-
1279
- if (bSpawnNiagara) {
1280
- FString SystemPath;
1281
- LocalPayload->TryGetStringField(TEXT("systemPath"), SystemPath);
1282
- if (SystemPath.IsEmpty()) {
1283
- SendAutomationResponse(RequestingSocket, RequestId, false,
1284
- TEXT("systemPath required"), nullptr,
1285
- TEXT("INVALID_ARGUMENT"));
1286
- return true;
1287
- }
1288
-
1289
- // Guard against non-existent assets to prevent LoadPackage warnings
1290
- if (!UEditorAssetLibrary::DoesAssetExist(SystemPath)) {
1291
- SendAutomationResponse(
1292
- RequestingSocket, RequestId, false,
1293
- FString::Printf(TEXT("Niagara system asset not found: %s"),
1294
- *SystemPath),
1295
- nullptr, TEXT("SYSTEM_NOT_FOUND"));
1296
- return true;
1297
- }
1298
-
1299
- // Location and optional rotation/scale
1300
- FVector Loc(0, 0, 0);
1301
- if (LocalPayload->HasField(TEXT("location"))) {
1302
- const TSharedPtr<FJsonValue> LocVal =
1303
- LocalPayload->TryGetField(TEXT("location"));
1304
- if (LocVal.IsValid()) {
1305
- if (LocVal->Type == EJson::Array) {
1306
- const TArray<TSharedPtr<FJsonValue>> &Arr = LocVal->AsArray();
1307
- if (Arr.Num() >= 3)
1308
- Loc = FVector((float)Arr[0]->AsNumber(), (float)Arr[1]->AsNumber(),
1309
- (float)Arr[2]->AsNumber());
1310
- } else if (LocVal->Type == EJson::Object) {
1311
- const TSharedPtr<FJsonObject> O = LocVal->AsObject();
1312
- if (O.IsValid())
1313
- Loc = FVector(
1314
- (float)(O->HasField(TEXT("x")) ? O->GetNumberField(TEXT("x"))
1315
- : 0.0),
1316
- (float)(O->HasField(TEXT("y")) ? O->GetNumberField(TEXT("y"))
1317
- : 0.0),
1318
- (float)(O->HasField(TEXT("z")) ? O->GetNumberField(TEXT("z"))
1319
- : 0.0));
1320
- }
1321
- }
1322
- }
1323
-
1324
- // Rotation may be an array
1325
- TArray<double> RotArr = {0, 0, 0};
1326
- const TArray<TSharedPtr<FJsonValue>> *RA = nullptr;
1327
- if (LocalPayload->TryGetArrayField(TEXT("rotation"), RA) && RA &&
1328
- RA->Num() >= 3) {
1329
- RotArr[0] = (*RA)[0]->AsNumber();
1330
- RotArr[1] = (*RA)[1]->AsNumber();
1331
- RotArr[2] = (*RA)[2]->AsNumber();
1332
- }
1333
-
1334
- // Scale may be an array or a single numeric value
1335
- TArray<double> ScaleArr = {1, 1, 1};
1336
- const TArray<TSharedPtr<FJsonValue>> *ScaleJsonArr = nullptr;
1337
- if (LocalPayload->TryGetArrayField(TEXT("scale"), ScaleJsonArr) &&
1338
- ScaleJsonArr && ScaleJsonArr->Num() >= 3) {
1339
- ScaleArr[0] = (*ScaleJsonArr)[0]->AsNumber();
1340
- ScaleArr[1] = (*ScaleJsonArr)[1]->AsNumber();
1341
- ScaleArr[2] = (*ScaleJsonArr)[2]->AsNumber();
1342
- } else if (LocalPayload->TryGetNumberField(TEXT("scale"), ScaleArr[0])) {
1343
- ScaleArr[1] = ScaleArr[2] = ScaleArr[0];
1344
- }
1345
-
1346
- const bool bAutoDestroy =
1347
- LocalPayload->HasField(TEXT("autoDestroy"))
1348
- ? LocalPayload->GetBoolField(TEXT("autoDestroy"))
1349
- : false;
1350
- FString AttachToActor;
1351
- LocalPayload->TryGetStringField(TEXT("attachToActor"), AttachToActor);
1352
-
1353
- #if WITH_EDITOR
1354
- if (!GEditor) {
1355
- SendAutomationResponse(RequestingSocket, RequestId, false,
1356
- TEXT("Editor not available"), nullptr,
1357
- TEXT("EDITOR_NOT_AVAILABLE"));
1358
- return true;
1359
- }
1360
- UEditorActorSubsystem *ActorSS =
1361
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1362
- if (!ActorSS) {
1363
- SendAutomationResponse(RequestingSocket, RequestId, false,
1364
- TEXT("EditorActorSubsystem not available"),
1365
- nullptr, TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
1366
- return true;
1367
- }
1368
-
1369
- UObject *NiagObj = UEditorAssetLibrary::LoadAsset(SystemPath);
1370
- if (!NiagObj) {
1371
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1372
- Resp->SetBoolField(TEXT("success"), false);
1373
- Resp->SetStringField(TEXT("error"),
1374
- TEXT("Niagara system asset not found"));
1375
- SendAutomationResponse(RequestingSocket, RequestId, false,
1376
- TEXT("Niagara system not found"), Resp,
1377
- TEXT("SYSTEM_NOT_FOUND"));
1378
- return true;
1379
- }
1380
-
1381
- const FRotator SpawnRot(static_cast<float>(RotArr[0]),
1382
- static_cast<float>(RotArr[1]),
1383
- static_cast<float>(RotArr[2]));
1384
- AActor *Spawned = SpawnActorInActiveWorld<AActor>(
1385
- ANiagaraActor::StaticClass(), Loc, SpawnRot);
1386
- if (!Spawned) {
1387
- SendAutomationResponse(RequestingSocket, RequestId, false,
1388
- TEXT("Failed to spawn NiagaraActor"), nullptr,
1389
- TEXT("SPAWN_FAILED"));
1390
- return true;
1391
- }
1392
-
1393
- UNiagaraComponent *NiComp =
1394
- Spawned->FindComponentByClass<UNiagaraComponent>();
1395
- if (NiComp && NiagObj->IsA<UNiagaraSystem>()) {
1396
- NiComp->SetAsset(Cast<UNiagaraSystem>(NiagObj));
1397
- NiComp->SetWorldScale3D(FVector(ScaleArr[0], ScaleArr[1], ScaleArr[2]));
1398
- NiComp->Activate(true); // Set to true
1399
- }
1400
-
1401
- if (!AttachToActor.IsEmpty()) {
1402
- AActor *Parent = nullptr;
1403
- TArray<AActor *> AllActors = ActorSS->GetAllLevelActors();
1404
- for (AActor *A : AllActors) {
1405
- if (A &&
1406
- A->GetActorLabel().Equals(AttachToActor, ESearchCase::IgnoreCase)) {
1407
- Parent = A;
1408
- break;
1409
- }
1410
- }
1411
- if (Parent) {
1412
- Spawned->AttachToActor(Parent,
1413
- FAttachmentTransformRules::KeepWorldTransform);
1414
- }
1415
- }
1416
-
1417
- // Set actor label
1418
- FString Name;
1419
- LocalPayload->TryGetStringField(TEXT("name"), Name);
1420
- if (Name.IsEmpty())
1421
- LocalPayload->TryGetStringField(TEXT("actorName"), Name);
1422
-
1423
- if (!Name.IsEmpty()) {
1424
- Spawned->SetActorLabel(Name);
1425
- } else {
1426
- Spawned->SetActorLabel(FString::Printf(
1427
- TEXT("Niagara_%lld"), FDateTime::Now().ToUnixTimestamp()));
1428
- }
1429
-
1430
- UE_LOG(LogMcpAutomationBridgeSubsystem, Display,
1431
- TEXT("spawn_niagara: Spawned actor '%s' (ID: %u)"),
1432
- *Spawned->GetActorLabel(), Spawned->GetUniqueID());
1433
-
1434
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1435
- Resp->SetBoolField(TEXT("success"), true);
1436
- Resp->SetStringField(TEXT("actor"), Spawned->GetActorLabel());
1437
- SendAutomationResponse(RequestingSocket, RequestId, true,
1438
- TEXT("Niagara spawned"), Resp, FString());
1439
- return true;
1440
- #else
1441
- SendAutomationResponse(RequestingSocket, RequestId, false,
1442
- TEXT("spawn_niagara requires editor build."),
1443
- nullptr, TEXT("NOT_IMPLEMENTED"));
1444
- return true;
1445
- #endif
1446
- }
1447
-
1448
- // CLEANUP EFFECTS - remove actors whose label starts with the provided filter
1449
- // (editor-only)
1450
- bool bCleanup = Lower.Equals(TEXT("cleanup"));
1451
- if (bIsCreateEffect) {
1452
- FString Sub;
1453
- LocalPayload->TryGetStringField(TEXT("action"), Sub);
1454
- if (Sub.ToLower() == TEXT("cleanup"))
1455
- bCleanup = true;
1456
- }
1457
-
1458
- if (bCleanup) {
1459
- FString Filter;
1460
- LocalPayload->TryGetStringField(TEXT("filter"), Filter);
1461
- // Allow empty filter as a no-op success
1462
- if (Filter.IsEmpty()) {
1463
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1464
- Resp->SetNumberField(TEXT("removed"), 0);
1465
- SendAutomationResponse(RequestingSocket, RequestId, true,
1466
- TEXT("Cleanup skipped (empty filter)"), Resp,
1467
- FString());
1468
- return true;
1469
- }
1470
- #if WITH_EDITOR
1471
- if (!GEditor) {
1472
- SendAutomationResponse(RequestingSocket, RequestId, false,
1473
- TEXT("Editor not available"), nullptr,
1474
- TEXT("EDITOR_NOT_AVAILABLE"));
1475
- return true;
1476
- }
1477
- UEditorActorSubsystem *ActorSS =
1478
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1479
- if (!ActorSS) {
1480
- SendAutomationResponse(RequestingSocket, RequestId, false,
1481
- TEXT("EditorActorSubsystem not available"),
1482
- nullptr, TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
1483
- return true;
1484
- }
1485
- TArray<AActor *> Actors = ActorSS->GetAllLevelActors();
1486
- TArray<FString> Removed;
1487
- for (AActor *A : Actors) {
1488
- if (!A) {
1489
- continue;
1490
- }
1491
- FString Label = A->GetActorLabel();
1492
- if (Label.IsEmpty()) {
1493
- continue;
1494
- }
1495
- if (!Label.StartsWith(Filter, ESearchCase::IgnoreCase)) {
1496
- continue;
1497
- }
1498
- bool bDel = ActorSS->DestroyActor(A);
1499
- if (bDel) {
1500
- Removed.Add(Label);
1501
- }
1502
- }
1503
- TArray<TSharedPtr<FJsonValue>> Arr;
1504
- for (const FString &S : Removed) {
1505
- Arr.Add(MakeShared<FJsonValueString>(S));
1506
- }
1507
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1508
- Resp->SetArrayField(TEXT("removedActors"), Arr);
1509
- Resp->SetNumberField(TEXT("removed"), Removed.Num());
1510
- SendAutomationResponse(
1511
- RequestingSocket, RequestId, true,
1512
- FString::Printf(TEXT("Cleanup completed (removed=%d)"), Removed.Num()),
1513
- Resp, FString());
1514
- return true;
1515
- #else
1516
- SendAutomationResponse(RequestingSocket, RequestId, false,
1517
- TEXT("cleanup requires editor build."), nullptr,
1518
- TEXT("NOT_IMPLEMENTED"));
1519
- return true;
1520
- #endif
1521
- }
1522
-
1523
- // STUB HANDLERS FOR TEST COVERAGE - NOW IMPLEMENTED
1524
- bool bCreateRibbon = Lower.Equals(TEXT("create_niagara_ribbon"));
1525
- bool bCreateFog = Lower.Equals(TEXT("create_volumetric_fog"));
1526
- bool bCreateTrail = Lower.Equals(TEXT("create_particle_trail"));
1527
- bool bCreateEnv = Lower.Equals(TEXT("create_environment_effect"));
1528
- bool bCreateImpact = Lower.Equals(TEXT("create_impact_effect"));
1529
-
1530
- if (bIsCreateEffect) {
1531
- FString Sub;
1532
- LocalPayload->TryGetStringField(TEXT("action"), Sub);
1533
- FString LSub = Sub.ToLower();
1534
- if (LSub == TEXT("create_niagara_ribbon"))
1535
- bCreateRibbon = true;
1536
- if (LSub == TEXT("create_volumetric_fog"))
1537
- bCreateFog = true;
1538
- if (LSub == TEXT("create_particle_trail"))
1539
- bCreateTrail = true;
1540
- if (LSub == TEXT("create_environment_effect"))
1541
- bCreateEnv = true;
1542
- if (LSub == TEXT("create_impact_effect"))
1543
- bCreateImpact = true;
1544
- }
1545
-
1546
- if (bCreateRibbon) {
1547
- // Require systemPath
1548
- return CreateNiagaraEffect(RequestId, Payload, RequestingSocket,
1549
- TEXT("create_niagara_ribbon"), FString());
1550
- }
1551
- if (bCreateFog) {
1552
- return CreateNiagaraEffect(RequestId, Payload, RequestingSocket,
1553
- TEXT("create_volumetric_fog"), FString());
1554
- }
1555
- if (bCreateTrail) {
1556
- return CreateNiagaraEffect(RequestId, Payload, RequestingSocket,
1557
- TEXT("create_particle_trail"), FString());
1558
- }
1559
- if (bCreateEnv) {
1560
- return CreateNiagaraEffect(RequestId, Payload, RequestingSocket,
1561
- TEXT("create_environment_effect"), FString());
1562
- }
1563
- if (bCreateImpact) {
1564
- return CreateNiagaraEffect(RequestId, Payload, RequestingSocket,
1565
- TEXT("create_impact_effect"), FString());
1566
- }
1567
-
1568
- return false;
1569
- }
1570
-
1571
- // Helper function to create Niagara effects with default systems
1572
- bool UMcpAutomationBridgeSubsystem::CreateNiagaraEffect(
1573
- const FString &RequestId, const TSharedPtr<FJsonObject> &Payload,
1574
- TSharedPtr<FMcpBridgeWebSocket> RequestingSocket, const FString &EffectName,
1575
- const FString &DefaultSystemPath) {
1576
- #if WITH_EDITOR
1577
- if (!GEditor) {
1578
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1579
- Resp->SetBoolField(TEXT("success"), false);
1580
- Resp->SetStringField(TEXT("error"), TEXT("Editor not available"));
1581
- SendAutomationResponse(RequestingSocket, RequestId, false,
1582
- TEXT("Editor not available"), Resp,
1583
- TEXT("EDITOR_NOT_AVAILABLE"));
1584
- return true;
1585
- }
1586
- UEditorActorSubsystem *ActorSS =
1587
- GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
1588
- if (!ActorSS) {
1589
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1590
- Resp->SetBoolField(TEXT("success"), false);
1591
- Resp->SetStringField(TEXT("error"),
1592
- TEXT("EditorActorSubsystem not available"));
1593
- SendAutomationResponse(RequestingSocket, RequestId, false,
1594
- TEXT("EditorActorSubsystem not available"), Resp,
1595
- TEXT("EDITOR_ACTOR_SUBSYSTEM_MISSING"));
1596
- return true;
1597
- }
1598
-
1599
- // Get custom system path or use default
1600
- FString SystemPath = DefaultSystemPath;
1601
- Payload->TryGetStringField(TEXT("systemPath"), SystemPath);
1602
-
1603
- if (SystemPath.IsEmpty()) {
1604
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1605
- Resp->SetBoolField(TEXT("success"), false);
1606
- Resp->SetStringField(
1607
- TEXT("error"),
1608
- FString::Printf(TEXT("systemPath is required for %s. Please provide a "
1609
- "valid asset path (e.g. /Game/Effects/MySystem)"),
1610
- *EffectName));
1611
- SendAutomationResponse(RequestingSocket, RequestId, false,
1612
- TEXT("systemPath required"), Resp,
1613
- TEXT("INVALID_ARGUMENT"));
1614
- return true;
1615
- }
1616
-
1617
- // Location
1618
- FVector Loc(0, 0, 0);
1619
- if (Payload->HasField(TEXT("location"))) {
1620
- const TSharedPtr<FJsonValue> LocVal =
1621
- Payload->TryGetField(TEXT("location"));
1622
- if (LocVal.IsValid()) {
1623
- if (LocVal->Type == EJson::Array) {
1624
- const TArray<TSharedPtr<FJsonValue>> &Arr = LocVal->AsArray();
1625
- if (Arr.Num() >= 3)
1626
- Loc = FVector((float)Arr[0]->AsNumber(), (float)Arr[1]->AsNumber(),
1627
- (float)Arr[2]->AsNumber());
1628
- } else if (LocVal->Type == EJson::Object) {
1629
- const TSharedPtr<FJsonObject> O = LocVal->AsObject();
1630
- if (O.IsValid())
1631
- Loc = FVector(
1632
- (float)(O->HasField(TEXT("x")) ? O->GetNumberField(TEXT("x"))
1633
- : 0.0),
1634
- (float)(O->HasField(TEXT("y")) ? O->GetNumberField(TEXT("y"))
1635
- : 0.0),
1636
- (float)(O->HasField(TEXT("z")) ? O->GetNumberField(TEXT("z"))
1637
- : 0.0));
1638
- }
1639
- }
1640
- }
1641
-
1642
- // Load the Niagara system
1643
- if (!UEditorAssetLibrary::DoesAssetExist(SystemPath)) {
1644
- SendAutomationResponse(
1645
- RequestingSocket, RequestId, false,
1646
- FString::Printf(TEXT("Niagara system asset not found: %s"),
1647
- *SystemPath),
1648
- nullptr, TEXT("SYSTEM_NOT_FOUND"));
1649
- return true;
1650
- }
1651
-
1652
- UObject *NiagObj = UEditorAssetLibrary::LoadAsset(SystemPath);
1653
- if (!NiagObj) {
1654
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1655
- Resp->SetBoolField(TEXT("success"), false);
1656
- Resp->SetStringField(TEXT("error"), TEXT("Niagara system asset not found"));
1657
- Resp->SetStringField(TEXT("systemPath"), SystemPath);
1658
- SendAutomationResponse(RequestingSocket, RequestId, false,
1659
- TEXT("Niagara system not found"), Resp,
1660
- TEXT("SYSTEM_NOT_FOUND"));
1661
- return true;
1662
- }
1663
-
1664
- // Spawn the actor
1665
- AActor *Spawned = SpawnActorInActiveWorld<AActor>(
1666
- ANiagaraActor::StaticClass(), Loc, FRotator::ZeroRotator);
1667
- if (!Spawned) {
1668
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1669
- Resp->SetBoolField(TEXT("success"), false);
1670
- Resp->SetStringField(TEXT("error"), TEXT("Failed to spawn Niagara actor"));
1671
- SendAutomationResponse(RequestingSocket, RequestId, false,
1672
- TEXT("Failed to spawn Niagara actor"), Resp,
1673
- TEXT("SPAWN_FAILED"));
1674
- return true;
1675
- }
1676
-
1677
- // Configure the Niagara component
1678
- UNiagaraComponent *NiComp =
1679
- Spawned->FindComponentByClass<UNiagaraComponent>();
1680
- if (NiComp && NiagObj->IsA<UNiagaraSystem>()) {
1681
- NiComp->SetAsset(Cast<UNiagaraSystem>(NiagObj));
1682
- NiComp->Activate(true);
1683
- }
1684
-
1685
- // Set actor label
1686
- FString Name;
1687
- Payload->TryGetStringField(TEXT("name"), Name);
1688
- if (Name.IsEmpty())
1689
- Payload->TryGetStringField(TEXT("actorName"), Name);
1690
-
1691
- if (!Name.IsEmpty()) {
1692
- Spawned->SetActorLabel(Name);
1693
- } else {
1694
- Spawned->SetActorLabel(FString::Printf(
1695
- TEXT("%s_%lld"), *EffectName.Replace(TEXT("create_"), TEXT("")),
1696
- FDateTime::Now().ToUnixTimestamp()));
1697
- }
1698
-
1699
- UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
1700
- TEXT("CreateNiagaraEffect: Spawned actor '%s' (ID: %u)"),
1701
- *Spawned->GetActorLabel(), Spawned->GetUniqueID());
1702
-
1703
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1704
- Resp->SetBoolField(TEXT("success"), true);
1705
- Resp->SetStringField(TEXT("effectType"), EffectName);
1706
- Resp->SetStringField(TEXT("systemPath"), SystemPath);
1707
- Resp->SetStringField(TEXT("actorName"), Spawned->GetActorLabel());
1708
- Resp->SetNumberField(TEXT("actorId"), Spawned->GetUniqueID());
1709
- SendAutomationResponse(
1710
- RequestingSocket, RequestId, true,
1711
- FString::Printf(TEXT("%s created successfully"), *EffectName), Resp,
1712
- FString());
1713
- return true;
1714
- #else
1715
- TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
1716
- Resp->SetBoolField(TEXT("success"), false);
1717
- Resp->SetStringField(TEXT("error"),
1718
- TEXT("Effect creation requires editor build"));
1719
- SendAutomationResponse(
1720
- RequestingSocket, RequestId, false,
1721
- TEXT("Effect creation not available in non-editor build"), Resp,
1722
- TEXT("NOT_AVAILABLE"));
1723
- return true;
1724
- #endif
1725
- }