unreal-engine-mcp-server 0.5.4 → 0.5.5

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