unreal-engine-mcp-server 0.4.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,12 +1,13 @@
1
- import { bestEffortInterpretedText, coerceBoolean, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
1
  import { ensureVector3 } from '../utils/validation.js';
3
- import { escapePythonString } from '../utils/python.js';
2
+ import { wasmIntegration } from '../wasm/index.js';
4
3
  export class LandscapeTools {
5
4
  bridge;
6
- constructor(bridge) {
5
+ automationBridge;
6
+ constructor(bridge, automationBridge) {
7
7
  this.bridge = bridge;
8
+ this.automationBridge = automationBridge;
8
9
  }
9
- // Create landscape with World Partition support (UE 5.6)
10
+ setAutomationBridge(automationBridge) { this.automationBridge = automationBridge; }
10
11
  async createLandscape(params) {
11
12
  const name = params.name?.trim();
12
13
  if (!name) {
@@ -24,270 +25,53 @@ export class LandscapeTools {
24
25
  error: 'Landscape sizeY must be a positive number'
25
26
  };
26
27
  }
28
+ if (!this.automationBridge) {
29
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
30
+ }
27
31
  const [locX, locY, locZ] = ensureVector3(params.location ?? [0, 0, 0], 'landscape location');
32
+ const zeroVector = [0, 0, 0];
33
+ const processedLocation = wasmIntegration.vectorAdd(zeroVector, [locX, locY, locZ]);
34
+ console.error('[WASM] Using vectorAdd for landscape positioning');
28
35
  const sectionsPerComponent = Math.max(1, Math.floor(params.sectionsPerComponent ?? 1));
29
36
  const quadsPerSection = Math.max(1, Math.floor(params.quadsPerSection ?? 63));
30
- const componentCount = Math.max(1, Math.floor(params.componentCount ?? 1));
31
- const defaultSize = 1000;
32
- const scaleX = params.sizeX ? Math.max(0.1, params.sizeX / defaultSize) : 1;
33
- const scaleY = params.sizeY ? Math.max(0.1, params.sizeY / defaultSize) : 1;
34
- const escapedName = escapePythonString(name);
35
- const escapedMaterial = params.materialPath && params.materialPath.trim().length > 0
36
- ? escapePythonString(params.materialPath.trim())
37
- : '';
38
- const runtimeGridFlag = params.runtimeGrid ? 'True' : 'False';
39
- const spatiallyLoadedFlag = params.isSpatiallyLoaded ? 'True' : 'False';
40
- const runtimeGridValue = params.runtimeGrid ? escapePythonString(params.runtimeGrid.trim()) : '';
41
- const dataLayerNames = Array.isArray(params.dataLayers)
42
- ? params.dataLayers
43
- .map(layer => layer?.trim())
44
- .filter((layer) => Boolean(layer))
45
- .map(layer => escapePythonString(layer))
46
- : [];
47
- const pythonScript = `
48
- import unreal
49
- import json
50
-
51
- result = {
52
- "success": False,
53
- "message": "",
54
- "error": "",
55
- "warnings": [],
56
- "details": [],
57
- "landscapeName": "",
58
- "landscapeActor": "",
59
- "worldPartition": False,
60
- "runtimeGridRequested": ${runtimeGridFlag},
61
- "spatiallyLoaded": ${spatiallyLoadedFlag}
62
- }
63
-
64
- try:
65
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
66
- world = editor_subsystem.get_editor_world() if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world') else None
67
- data_layer_manager = None
68
- world_partition = None
69
- if world:
70
- # Try multiple methods to access World Partition (UE 5.6+)
71
- try:
72
- # Method 1: Try get_world_partition() if it exists
73
- if hasattr(world, 'get_world_partition'):
74
- world_partition = world.get_world_partition()
75
- except (AttributeError, Exception):
76
- pass
77
-
78
- if not world_partition:
79
- try:
80
- # Method 2: Try WorldPartitionSubsystem
81
- wp_subsystem = unreal.get_editor_subsystem(unreal.WorldPartitionSubsystem)
82
- if wp_subsystem:
83
- world_partition = wp_subsystem.get_world_partition(world)
84
- except (AttributeError, Exception):
85
- pass
86
-
87
- if not world_partition:
88
- try:
89
- # Method 3: Check if world has world_partition property
90
- if hasattr(world, 'world_partition'):
91
- world_partition = world.world_partition
92
- except (AttributeError, Exception):
93
- pass
94
-
95
- result["worldPartition"] = world_partition is not None
96
-
97
- if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
98
- try:
99
- data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
100
- except Exception as dlm_error:
101
- result["warnings"].append(f"Data layer manager unavailable: {dlm_error}")
102
-
103
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
104
- if not actor_subsystem:
105
- result["error"] = "EditorActorSubsystem unavailable"
106
- else:
107
- existing = None
108
- try:
109
- for actor in actor_subsystem.get_all_level_actors():
110
- if actor and actor.get_actor_label() == "${escapedName}":
111
- existing = actor
112
- break
113
- except Exception as scan_error:
114
- result["warnings"].append(f"Actor scan failed: {scan_error}")
115
-
116
- if existing:
117
- result["success"] = True
118
- result["message"] = "Landscape already exists"
119
- result["landscapeName"] = existing.get_actor_label()
120
- try:
121
- result["landscapeActor"] = existing.get_path_name()
122
- except Exception:
123
- pass
124
- else:
125
- landscape_class = getattr(unreal, "Landscape", None)
126
- if not landscape_class:
127
- result["error"] = "Landscape class unavailable"
128
- else:
129
- location = unreal.Vector(${locX}, ${locY}, ${locZ})
130
- rotation = unreal.Rotator(0.0, 0.0, 0.0)
131
- landscape_actor = actor_subsystem.spawn_actor_from_class(landscape_class, location, rotation)
132
- if not landscape_actor:
133
- result["error"] = "Failed to spawn landscape actor"
134
- else:
135
- # Set label first
136
- try:
137
- landscape_actor.set_actor_label("${escapedName}", True)
138
- except TypeError:
139
- landscape_actor.set_actor_label("${escapedName}")
140
- except Exception as label_error:
141
- result["warnings"].append(f"Failed to set landscape label: {label_error}")
142
-
143
- # Fix component registration by forcing re-registration
144
- # This addresses the "RegisterComponentWithWorld: Trying to register component with IsValid() == false" warning
145
- try:
146
- # Get landscape components and re-register them
147
- landscape_components = landscape_actor.get_components_by_class(unreal.LandscapeComponent)
148
- if landscape_components:
149
- for component in landscape_components:
150
- if hasattr(component, 'register_component'):
151
- try:
152
- component.register_component()
153
- except Exception:
154
- pass
155
- else:
156
- # If no components yet, this is expected for LandscapePlaceholder
157
- # The landscape needs to be "finalized" via editor tools or console commands
158
- result["details"].append("Landscape placeholder created - finalize via editor for full functionality")
159
- except Exception as comp_error:
160
- # Component registration is best-effort; not critical
161
- result["details"].append(f"Component registration attempted (editor finalization may be needed)")
162
-
163
- try:
164
- landscape_actor.set_actor_scale3d(unreal.Vector(${scaleX.toFixed(4)}, ${scaleY.toFixed(4)}, 1.0))
165
- result["details"].append(f"Actor scale set to (${scaleX.toFixed(2)}, ${scaleY.toFixed(2)}, 1.0)")
166
- except Exception as scale_error:
167
- result["warnings"].append(f"Failed to set landscape scale: {scale_error}")
168
-
169
- # Workaround for LandscapeEditorSubsystem Python API limitation
170
- # Use direct property manipulation instead
171
- landscape_configured = False
172
- try:
173
- # Try LandscapeEditorSubsystem if available (may not be in Python API)
174
- landscape_editor = unreal.get_editor_subsystem(unreal.LandscapeEditorSubsystem)
175
- if landscape_editor:
176
- try:
177
- landscape_editor.set_component_size(${sectionsPerComponent}, ${quadsPerSection})
178
- landscape_editor.set_component_count(${componentCount}, ${componentCount})
179
- result["details"].append(f"Component size ${sectionsPerComponent}x${quadsPerSection}, count ${componentCount}x${componentCount}")
180
- landscape_configured = True
181
- except Exception as config_error:
182
- result["details"].append(f"LandscapeEditorSubsystem method limited: {config_error}")
183
- except (AttributeError, Exception):
184
- # Expected - LandscapeEditorSubsystem not available in Python API
185
- pass
186
-
187
- # Fallback: Configure via properties if subsystem not available
188
- if not landscape_configured:
189
- try:
190
- # Set component properties directly
191
- if hasattr(landscape_actor, 'set_editor_property'):
192
- # Note: These properties may not be directly editable post-spawn
193
- # This is documented UE limitation - landscape config is best done via editor tools
194
- result["details"].append(f"Landscape spawned (config via editor tools recommended for ${sectionsPerComponent}x${quadsPerSection} components)")
195
- except Exception:
196
- pass
197
-
198
- ${escapedMaterial ? `try:
199
- material = unreal.EditorAssetLibrary.load_asset("${escapedMaterial}")
200
- if material:
201
- try:
202
- landscape_actor.set_landscape_material(material)
203
- except Exception:
204
- landscape_actor.editor_set_landscape_material(material)
205
- result["details"].append("Landscape material applied")
206
- else:
207
- result["warnings"].append("Landscape material asset not found: ${escapedMaterial}")
208
- except Exception as material_error:
209
- result["warnings"].append(f"Failed to apply landscape material: {material_error}")
210
- ` : ''}
211
- ${runtimeGridValue ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
212
- try:
213
- unreal.WorldPartitionBlueprintLibrary.set_actor_runtime_grid(landscape_actor, "${runtimeGridValue}")
214
- result["details"].append("Runtime grid assigned: ${runtimeGridValue}")
215
- except Exception as grid_error:
216
- result["warnings"].append(f"Failed to assign runtime grid: {grid_error}")
217
- ` : ''}
218
- ${params.isSpatiallyLoaded ? `if result["worldPartition"] and hasattr(unreal, "WorldPartitionBlueprintLibrary"):
219
- try:
220
- unreal.WorldPartitionBlueprintLibrary.set_actor_spatially_loaded(landscape_actor, True)
221
- result["details"].append("Actor marked as spatially loaded")
222
- except Exception as spatial_error:
223
- result["warnings"].append(f"Failed to mark as spatially loaded: {spatial_error}")
224
- ` : ''}
225
- ${dataLayerNames.length ? `if result["worldPartition"] and data_layer_manager:
226
- for layer_name in ${JSON.stringify(dataLayerNames)}:
227
- try:
228
- data_layer = data_layer_manager.get_data_layer(layer_name)
229
- if data_layer:
230
- unreal.WorldPartitionBlueprintLibrary.add_actor_to_data_layer(landscape_actor, data_layer)
231
- result["details"].append(f"Added to data layer {layer_name}")
232
- else:
233
- result["warnings"].append(f"Data layer not found: {layer_name}")
234
- except Exception as data_layer_error:
235
- result["warnings"].append(f"Failed to assign data layer {layer_name}: {data_layer_error}")
236
- ` : ''}
237
-
238
- try:
239
- result["landscapeName"] = landscape_actor.get_actor_label()
240
- result["landscapeActor"] = landscape_actor.get_path_name()
241
- except Exception:
242
- pass
243
-
244
- result["success"] = True
245
- result["message"] = "Landscape actor created"
246
- except Exception as e:
247
- result["error"] = str(e)
248
-
249
- if result.get("success"):
250
- result.pop("error", None)
251
- else:
252
- if not result.get("error"):
253
- result["error"] = "Failed to create landscape actor"
254
- if not result.get("message"):
255
- result["message"] = result["error"]
256
-
257
- if not result.get("warnings"):
258
- result.pop("warnings", None)
259
- if not result.get("details"):
260
- result.pop("details", None)
261
-
262
- print("RESULT:" + json.dumps(result))
263
- `.trim();
264
37
  try {
265
- const response = await this.bridge.executePython(pythonScript);
266
- const interpreted = interpretStandardResult(response, {
267
- successMessage: 'Landscape actor created',
268
- failureMessage: 'Failed to create landscape actor'
38
+ const componentsX = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeX ?? 1000) / 1000)))));
39
+ const componentsY = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeY ?? 1000) / 1000)))));
40
+ const quadsPerComponent = quadsPerSection;
41
+ const payload = {
42
+ name,
43
+ x: processedLocation[0],
44
+ y: processedLocation[1],
45
+ z: processedLocation[2],
46
+ componentsX,
47
+ componentsY,
48
+ quadsPerComponent,
49
+ sectionsPerComponent,
50
+ materialPath: params.materialPath || ''
51
+ };
52
+ const response = await this.automationBridge.sendAutomationRequest('create_landscape', payload, {
53
+ timeoutMs: 60000
269
54
  });
270
- if (!interpreted.success) {
55
+ if (response.success === false) {
271
56
  return {
272
57
  success: false,
273
- error: interpreted.error || interpreted.message
58
+ error: response.error || response.message || 'Failed to create landscape actor'
274
59
  };
275
60
  }
276
61
  const result = {
277
62
  success: true,
278
- message: interpreted.message,
279
- landscapeName: coerceString(interpreted.payload.landscapeName) ?? name,
280
- worldPartition: coerceBoolean(interpreted.payload.worldPartition)
63
+ message: response.message || 'Landscape actor created',
64
+ landscapeName: response.landscapeName || name,
65
+ worldPartition: response.worldPartition ?? params.enableWorldPartition ?? false
281
66
  };
282
- const actorPath = coerceString(interpreted.payload.landscapeActor);
283
- if (actorPath) {
284
- result.landscapeActor = actorPath;
67
+ if (response.landscapeActor) {
68
+ result.landscapeActor = response.landscapeActor;
285
69
  }
286
- if (interpreted.warnings?.length) {
287
- result.warnings = interpreted.warnings;
70
+ if (response.warnings) {
71
+ result.warnings = response.warnings;
288
72
  }
289
- if (interpreted.details?.length) {
290
- result.details = interpreted.details;
73
+ if (response.details) {
74
+ result.details = response.details;
291
75
  }
292
76
  if (params.runtimeGrid) {
293
77
  result.runtimeGrid = params.runtimeGrid;
@@ -300,78 +84,202 @@ print("RESULT:" + json.dumps(result))
300
84
  catch (error) {
301
85
  return {
302
86
  success: false,
303
- error: `Failed to create landscape actor: ${error}`
87
+ error: `Failed to create landscape actor: ${error instanceof Error ? error.message : String(error)}`
304
88
  };
305
89
  }
306
90
  }
307
- // Sculpt landscape
308
- async sculptLandscape(_params) {
309
- return { success: false, error: 'sculptLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
91
+ async sculptLandscape(params) {
92
+ const [x, y, z] = ensureVector3(params.location ?? [0, 0, 0], 'sculpt location');
93
+ const tool = (params.tool || '').trim();
94
+ const lowerTool = tool.toLowerCase();
95
+ const validTools = new Set(['sculpt', 'smooth', 'flatten', 'ramp', 'erosion', 'hydro', 'noise', 'raise', 'lower']);
96
+ const isValidTool = lowerTool.length > 0 && validTools.has(lowerTool);
97
+ if (!isValidTool) {
98
+ return {
99
+ success: false,
100
+ error: `Invalid sculpt tool: ${params.tool}`
101
+ };
102
+ }
103
+ if (!this.automationBridge) {
104
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
105
+ }
106
+ const payload = {
107
+ landscapeName: params.landscapeName?.trim(),
108
+ toolMode: tool,
109
+ brushRadius: params.brushSize ?? params.radius ?? 1000,
110
+ brushFalloff: params.brushFalloff ?? 0.5,
111
+ strength: params.strength ?? 0.1,
112
+ location: { x, y, z }
113
+ };
114
+ const response = await this.automationBridge.sendAutomationRequest('sculpt_landscape', payload);
115
+ if (!response.success) {
116
+ return {
117
+ success: false,
118
+ error: response.error || 'Failed to sculpt landscape'
119
+ };
120
+ }
121
+ return {
122
+ success: true,
123
+ message: `Sculpting applied to ${params.landscapeName}`,
124
+ details: response
125
+ };
310
126
  }
311
- // Paint landscape
312
- async paintLandscape(_params) {
313
- return { success: false, error: 'paintLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
127
+ async paintLandscape(params) {
128
+ if (!this.automationBridge) {
129
+ throw new Error('Automation Bridge not available.');
130
+ }
131
+ const [x, y] = ensureVector3(params.position, 'paint position');
132
+ const radius = params.brushSize ?? params.radius ?? 1000;
133
+ const minX = Math.floor(x - radius);
134
+ const maxX = Math.floor(x + radius);
135
+ const minY = Math.floor(y - radius);
136
+ const maxY = Math.floor(y + radius);
137
+ const payload = {
138
+ landscapeName: params.landscapeName?.trim(),
139
+ layerName: params.layerName?.trim(),
140
+ region: { minX, minY, maxX, maxY },
141
+ strength: params.strength ?? 1.0
142
+ };
143
+ const response = await this.automationBridge.sendAutomationRequest('paint_landscape_layer', payload);
144
+ if (!response.success) {
145
+ return {
146
+ success: false,
147
+ error: response.error || 'Failed to paint landscape layer'
148
+ };
149
+ }
150
+ return {
151
+ success: true,
152
+ message: `Painted layer ${params.layerName}`,
153
+ details: response
154
+ };
314
155
  }
315
- // Add landscape layer
316
- async addLandscapeLayer(params) {
317
- const commands = [];
318
- commands.push(`AddLandscapeLayer ${params.landscapeName} ${params.layerName}`);
319
- if (params.weightMapPath) {
320
- commands.push(`SetLayerWeightMap ${params.layerName} ${params.weightMapPath}`);
156
+ async createProceduralTerrain(params) {
157
+ if (!this.automationBridge) {
158
+ throw new Error('Automation Bridge not available. Procedural terrain creation requires plugin support.');
321
159
  }
322
- if (params.blendMode) {
323
- commands.push(`SetLayerBlendMode ${params.layerName} ${params.blendMode}`);
160
+ try {
161
+ const payload = {
162
+ name: params.name,
163
+ location: params.location || [0, 0, 0],
164
+ sizeX: params.sizeX || 2000,
165
+ sizeY: params.sizeY || 2000,
166
+ subdivisions: params.subdivisions || 50,
167
+ heightFunction: params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30',
168
+ material: params.material,
169
+ ...params.settings
170
+ };
171
+ const response = await this.automationBridge.sendAutomationRequest('create_procedural_terrain', payload, {
172
+ timeoutMs: 120000
173
+ });
174
+ if (response.success === false) {
175
+ return {
176
+ success: false,
177
+ error: response.error || response.message || 'Failed to create procedural terrain',
178
+ message: response.message || 'Failed to create procedural terrain'
179
+ };
180
+ }
181
+ const result = response.result;
182
+ return {
183
+ success: true,
184
+ message: response.message || `Created procedural terrain '${params.name}'`,
185
+ actorName: result?.actor_name,
186
+ vertices: result?.vertices,
187
+ triangles: result?.triangles,
188
+ size: result?.size,
189
+ subdivisions: result?.subdivisions,
190
+ details: result
191
+ };
192
+ }
193
+ catch (error) {
194
+ return {
195
+ success: false,
196
+ error: `Failed to create procedural terrain: ${error instanceof Error ? error.message : String(error)}`
197
+ };
324
198
  }
325
- await this.bridge.executeConsoleCommands(commands);
326
- return { success: true, message: `Layer ${params.layerName} added to landscape` };
327
199
  }
328
- // Create landscape spline
329
- async createLandscapeSpline(params) {
330
- const commands = [];
331
- commands.push(`CreateLandscapeSpline ${params.landscapeName} ${params.splineName}`);
332
- for (const point of params.points) {
333
- commands.push(`AddSplinePoint ${params.splineName} ${point.join(' ')}`);
200
+ async createLandscapeGrassType(params) {
201
+ if (!this.automationBridge) {
202
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
334
203
  }
335
- if (params.width !== undefined) {
336
- commands.push(`SetSplineWidth ${params.splineName} ${params.width}`);
204
+ const name = typeof params.name === 'string' ? params.name.trim() : '';
205
+ if (!name) {
206
+ return { success: false, error: 'Grass type name is required' };
337
207
  }
338
- if (params.falloffWidth !== undefined) {
339
- commands.push(`SetSplineFalloffWidth ${params.splineName} ${params.falloffWidth}`);
208
+ const meshPathRaw = typeof params.meshPath === 'string' && params.meshPath.trim().length > 0
209
+ ? params.meshPath.trim()
210
+ : (typeof params.path === 'string' && params.path.trim().length > 0
211
+ ? params.path.trim()
212
+ : (typeof params.staticMesh === 'string' && params.staticMesh.trim().length > 0
213
+ ? params.staticMesh.trim()
214
+ : ''));
215
+ if (!meshPathRaw) {
216
+ return { success: false, error: 'meshPath is required to create a landscape grass type' };
340
217
  }
341
- if (params.meshPath) {
342
- commands.push(`SetSplineMesh ${params.splineName} ${params.meshPath}`);
218
+ try {
219
+ const response = await this.automationBridge.sendAutomationRequest('create_landscape_grass_type', {
220
+ name,
221
+ meshPath: meshPathRaw,
222
+ density: params.density || 1.0,
223
+ minScale: params.minScale || 0.8,
224
+ maxScale: params.maxScale || 1.2
225
+ }, { timeoutMs: 90000 });
226
+ if (response && response.success === false) {
227
+ return {
228
+ success: false,
229
+ error: response.error || response.message || 'Failed to create landscape grass type'
230
+ };
231
+ }
232
+ const result = response.result;
233
+ return {
234
+ success: true,
235
+ message: response?.message || `Landscape grass type '${name}' created`,
236
+ assetPath: result?.asset_path || response?.assetPath || response?.asset_path
237
+ };
238
+ }
239
+ catch (error) {
240
+ return {
241
+ success: false,
242
+ error: `Failed to create landscape grass type: ${error instanceof Error ? error.message : String(error)}`
243
+ };
343
244
  }
344
- await this.bridge.executeConsoleCommands(commands);
345
- return { success: true, message: `Landscape spline ${params.splineName} created` };
346
- }
347
- // Import heightmap
348
- async importHeightmap(params) {
349
- const scale = params.scale || [100, 100, 100];
350
- const command = `ImportLandscapeHeightmap ${params.landscapeName} ${params.heightmapPath} ${scale.join(' ')}`;
351
- return this.bridge.executeConsoleCommand(command);
352
- }
353
- // Export heightmap
354
- async exportHeightmap(params) {
355
- const format = params.format || 'PNG';
356
- const command = `ExportLandscapeHeightmap ${params.landscapeName} ${params.exportPath} ${format}`;
357
- return this.bridge.executeConsoleCommand(command);
358
245
  }
359
- // Set landscape LOD
360
- async setLandscapeLOD(params) {
361
- const commands = [];
362
- if (params.lodBias !== undefined) {
363
- commands.push(`SetLandscapeLODBias ${params.landscapeName} ${params.lodBias}`);
246
+ async setLandscapeMaterial(params) {
247
+ const landscapeName = typeof params.landscapeName === 'string' ? params.landscapeName.trim() : '';
248
+ const materialPath = typeof params.materialPath === 'string' ? params.materialPath.trim() : '';
249
+ if (!landscapeName) {
250
+ return { success: false, error: 'Landscape name is required' };
364
251
  }
365
- if (params.forcedLOD !== undefined) {
366
- commands.push(`SetLandscapeForcedLOD ${params.landscapeName} ${params.forcedLOD}`);
252
+ if (!materialPath) {
253
+ return { success: false, error: 'materialPath is required' };
367
254
  }
368
- if (params.lodDistribution !== undefined) {
369
- commands.push(`SetLandscapeLODDistribution ${params.landscapeName} ${params.lodDistribution}`);
255
+ if (!this.automationBridge) {
256
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
257
+ }
258
+ try {
259
+ const response = await this.automationBridge.sendAutomationRequest('set_landscape_material', {
260
+ landscapeName,
261
+ materialPath
262
+ }, { timeoutMs: 60000 });
263
+ if (response && response.success === false) {
264
+ return {
265
+ success: false,
266
+ error: response.error || response.message || 'Failed to set landscape material'
267
+ };
268
+ }
269
+ return {
270
+ success: true,
271
+ message: response?.message || `Landscape material set on '${landscapeName}'`,
272
+ landscapeName: response?.landscapeName || landscapeName,
273
+ materialPath: response?.materialPath || materialPath
274
+ };
275
+ }
276
+ catch (error) {
277
+ return {
278
+ success: false,
279
+ error: `Failed to set landscape material: ${error instanceof Error ? error.message : String(error)}`
280
+ };
370
281
  }
371
- await this.bridge.executeConsoleCommands(commands);
372
- return { success: true, message: 'Landscape LOD settings updated' };
373
282
  }
374
- // Create landscape grass
375
283
  async createLandscapeGrass(params) {
376
284
  const commands = [];
377
285
  commands.push(`CreateLandscapeGrass ${params.landscapeName} ${params.grassType}`);
@@ -387,7 +295,6 @@ print("RESULT:" + json.dumps(result))
387
295
  await this.bridge.executeConsoleCommands(commands);
388
296
  return { success: true, message: `Grass type ${params.grassType} created on landscape` };
389
297
  }
390
- // Landscape collision
391
298
  async updateLandscapeCollision(params) {
392
299
  const commands = [];
393
300
  if (params.collisionMipLevel !== undefined) {
@@ -400,7 +307,6 @@ print("RESULT:" + json.dumps(result))
400
307
  await this.bridge.executeConsoleCommands(commands);
401
308
  return { success: true, message: 'Landscape collision updated' };
402
309
  }
403
- // Retopologize landscape
404
310
  async retopologizeLandscape(params) {
405
311
  const commands = [];
406
312
  if (params.targetTriangleCount !== undefined) {
@@ -413,7 +319,6 @@ print("RESULT:" + json.dumps(result))
413
319
  await this.bridge.executeConsoleCommands(commands);
414
320
  return { success: true, message: 'Landscape retopologized' };
415
321
  }
416
- // Create water body
417
322
  async createWaterBody(params) {
418
323
  const loc = params.location || [0, 0, 0];
419
324
  const size = params.size || [1000, 1000];
@@ -421,111 +326,39 @@ print("RESULT:" + json.dumps(result))
421
326
  const command = `CreateWaterBody ${params.type} ${params.name} ${loc.join(' ')} ${size.join(' ')} ${depth}`;
422
327
  return this.bridge.executeConsoleCommand(command);
423
328
  }
424
- // World Partition support for landscapes (UE 5.6)
425
329
  async configureWorldPartition(params) {
330
+ if (!this.automationBridge) {
331
+ throw new Error('Automation Bridge not available. World Partition operations require plugin support.');
332
+ }
426
333
  try {
427
- const pythonScript = `
428
- import unreal
429
- import json
430
-
431
- result = {'success': False, 'error': 'Landscape not found'}
432
-
433
- try:
434
- # Get the landscape actor using modern EditorActorSubsystem
435
- actors = []
436
- try:
437
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
438
- if actor_subsystem and hasattr(actor_subsystem, 'get_all_level_actors'):
439
- actors = actor_subsystem.get_all_level_actors()
440
- except Exception:
441
- actors = []
442
- landscape = None
443
-
444
- for actor in actors:
445
- if actor.get_name() == "${params.landscapeName}" or actor.get_actor_label() == "${params.landscapeName}":
446
- if isinstance(actor, unreal.LandscapeProxy) or isinstance(actor, unreal.Landscape):
447
- landscape = actor
448
- break
449
-
450
- if landscape:
451
- changes_made = []
452
-
453
- # Configure spatial loading (UE 5.6)
454
- if ${params.enableSpatialLoading !== undefined ? 'True' : 'False'}:
455
- try:
456
- landscape.set_editor_property('is_spatially_loaded', ${params.enableSpatialLoading || false})
457
- changes_made.append("Spatial loading: ${params.enableSpatialLoading}")
458
- except:
459
- pass
460
-
461
- # Set runtime grid (UE 5.6 World Partition)
462
- if "${params.runtimeGrid || ''}":
463
- try:
464
- landscape.set_editor_property('runtime_grid', unreal.Name("${params.runtimeGrid}"))
465
- changes_made.append("Runtime grid: ${params.runtimeGrid}")
466
- except:
467
- pass
468
-
469
- # Configure data layers (UE 5.6)
470
- if ${params.dataLayers ? 'True' : 'False'}:
471
- try:
472
- # Try modern subsystem first
473
- try:
474
- world = None
475
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
476
- if editor_subsystem and hasattr(editor_subsystem, 'get_editor_world'):
477
- world = editor_subsystem.get_editor_world()
478
- if world is None:
479
- world = unreal.EditorSubsystemLibrary.get_editor_world()
480
- except Exception:
481
- world = unreal.EditorSubsystemLibrary.get_editor_world()
482
- data_layer_manager = unreal.WorldPartitionBlueprintLibrary.get_data_layer_manager(world)
483
- if data_layer_manager:
484
- # Note: Full data layer API requires additional setup
485
- changes_made.append("Data layers: Requires manual configuration")
486
- except:
487
- pass
488
-
489
- if changes_made:
490
- result = {
491
- 'success': True,
492
- 'message': 'World Partition configured',
493
- 'changes': changes_made
494
- }
495
- else:
496
- result = {
497
- 'success': False,
498
- 'error': 'No World Partition changes applied'
499
- }
500
-
501
- except Exception as e:
502
- result = {'success': False, 'error': str(e)}
503
-
504
- print('RESULT:' + json.dumps(result))
505
- `.trim();
506
- const response = await this.bridge.executePython(pythonScript);
507
- const interpreted = interpretStandardResult(response, {
508
- successMessage: 'World Partition configuration attempted',
509
- failureMessage: 'World Partition configuration failed'
334
+ const response = await this.automationBridge.sendAutomationRequest('configure_landscape_world_partition', {
335
+ landscapeName: params.landscapeName,
336
+ enableSpatialLoading: params.enableSpatialLoading,
337
+ runtimeGrid: params.runtimeGrid || '',
338
+ dataLayers: params.dataLayers || [],
339
+ streamingDistance: params.streamingDistance
340
+ }, {
341
+ timeoutMs: 60000
510
342
  });
511
- if (interpreted.success) {
512
- return interpreted.payload;
343
+ if (response.success === false) {
344
+ return {
345
+ success: false,
346
+ error: response.error || response.message || 'World Partition configuration failed'
347
+ };
513
348
  }
514
349
  return {
515
- success: false,
516
- error: interpreted.error ?? 'World Partition configuration failed',
517
- details: bestEffortInterpretedText(interpreted)
350
+ success: true,
351
+ message: response.message || 'World Partition configured',
352
+ changes: response.changes
518
353
  };
519
354
  }
520
355
  catch (err) {
521
- return { success: false, error: `Failed to configure World Partition: ${err}` };
356
+ return { success: false, error: `Failed to configure World Partition: ${err instanceof Error ? err.message : String(err)}` };
522
357
  }
523
358
  }
524
- // Set landscape data layers (UE 5.6)
525
359
  async setDataLayers(params) {
526
360
  try {
527
361
  const commands = [];
528
- // Use console commands for data layer management
529
362
  if (params.operation === 'set' || params.operation === 'add') {
530
363
  for (const layerName of params.dataLayerNames) {
531
364
  commands.push(`wp.Runtime.SetDataLayerRuntimeState Loaded ${layerName}`);
@@ -536,7 +369,6 @@ print('RESULT:' + json.dumps(result))
536
369
  commands.push(`wp.Runtime.SetDataLayerRuntimeState Unloaded ${layerName}`);
537
370
  }
538
371
  }
539
- // Execute commands
540
372
  await this.bridge.executeConsoleCommands(commands);
541
373
  return {
542
374
  success: true,
@@ -548,18 +380,15 @@ print('RESULT:' + json.dumps(result))
548
380
  return { success: false, error: `Failed to manage data layers: ${err}` };
549
381
  }
550
382
  }
551
- // Configure landscape streaming cells (UE 5.6 World Partition)
552
383
  async configureStreamingCells(params) {
553
384
  const commands = [];
554
- // World Partition runtime commands
555
385
  if (params.loadingRange !== undefined) {
556
386
  commands.push(`wp.Runtime.OverrideRuntimeSpatialHashLoadingRange -grid=0 -range=${params.loadingRange}`);
557
387
  }
558
388
  if (params.enableHLOD !== undefined) {
559
389
  commands.push(`wp.Runtime.HLOD ${params.enableHLOD ? '1' : '0'}`);
560
390
  }
561
- // Debug visualization commands
562
- commands.push('wp.Runtime.ToggleDrawRuntimeHash2D'); // Show 2D grid
391
+ commands.push('wp.Runtime.ToggleDrawRuntimeHash2D');
563
392
  try {
564
393
  await this.bridge.executeConsoleCommands(commands);
565
394
  return {
@@ -576,5 +405,51 @@ print('RESULT:' + json.dumps(result))
576
405
  return { success: false, error: `Failed to configure streaming cells: ${err}` };
577
406
  }
578
407
  }
408
+ async modifyHeightmap(params) {
409
+ if (!this.automationBridge) {
410
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
411
+ }
412
+ const { landscapeName, heightData, minX, minY, maxX, maxY } = params;
413
+ if (!landscapeName) {
414
+ return { success: false, error: 'Landscape name is required' };
415
+ }
416
+ if (!heightData || !Array.isArray(heightData) || heightData.length === 0) {
417
+ return { success: false, error: 'heightData array is required' };
418
+ }
419
+ const width = maxX - minX + 1;
420
+ const height = maxY - minY + 1;
421
+ if (heightData.length !== width * height) {
422
+ return {
423
+ success: false,
424
+ error: `Height data length (${heightData.length}) does not match region dimensions (${width}x${height} = ${width * height})`
425
+ };
426
+ }
427
+ try {
428
+ const response = await this.automationBridge.sendAutomationRequest('modify_heightmap', {
429
+ landscapeName,
430
+ heightData,
431
+ minX,
432
+ minY,
433
+ maxX,
434
+ maxY,
435
+ updateNormals: params.updateNormals ?? true
436
+ }, {
437
+ timeoutMs: 60000
438
+ });
439
+ if (response.success === false) {
440
+ return {
441
+ success: false,
442
+ error: response.error || response.message || 'Failed to modify heightmap'
443
+ };
444
+ }
445
+ return {
446
+ success: true,
447
+ message: response.message || 'Heightmap modified successfully'
448
+ };
449
+ }
450
+ catch (err) {
451
+ return { success: false, error: `Failed to modify heightmap: ${err instanceof Error ? err.message : String(err)}` };
452
+ }
453
+ }
579
454
  }
580
455
  //# sourceMappingURL=landscape.js.map