unreal-engine-mcp-server 0.4.6 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter.yml +148 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +23 -0
  19. package/.github/workflows/labeler.yml +16 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +12 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +269 -22
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -72
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -604
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5475 -1627
  97. package/dist/tools/consolidated-tool-definitions.js +829 -482
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1009
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +45 -0
  161. package/dist/tools/logs.js +210 -0
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +195 -11
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -649
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -500
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1122
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +219 -0
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +250 -13
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -572
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,12 +1,12 @@
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';
4
2
  export class LandscapeTools {
5
3
  bridge;
6
- constructor(bridge) {
4
+ automationBridge;
5
+ constructor(bridge, automationBridge) {
7
6
  this.bridge = bridge;
7
+ this.automationBridge = automationBridge;
8
8
  }
9
- // Create landscape with World Partition support (UE 5.6)
9
+ setAutomationBridge(automationBridge) { this.automationBridge = automationBridge; }
10
10
  async createLandscape(params) {
11
11
  const name = params.name?.trim();
12
12
  if (!name) {
@@ -24,270 +24,50 @@ export class LandscapeTools {
24
24
  error: 'Landscape sizeY must be a positive number'
25
25
  };
26
26
  }
27
+ if (!this.automationBridge) {
28
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
29
+ }
27
30
  const [locX, locY, locZ] = ensureVector3(params.location ?? [0, 0, 0], 'landscape location');
28
31
  const sectionsPerComponent = Math.max(1, Math.floor(params.sectionsPerComponent ?? 1));
29
32
  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
33
  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'
34
+ const componentsX = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeX ?? 1000) / 1000)))));
35
+ const componentsY = Math.max(1, Math.floor((params.componentCount ?? Math.max(1, Math.floor((params.sizeY ?? 1000) / 1000)))));
36
+ const quadsPerComponent = quadsPerSection;
37
+ const payload = {
38
+ name,
39
+ x: locX,
40
+ y: locY,
41
+ z: locZ,
42
+ componentsX,
43
+ componentsY,
44
+ quadsPerComponent,
45
+ sectionsPerComponent,
46
+ materialPath: params.materialPath || ''
47
+ };
48
+ const response = await this.automationBridge.sendAutomationRequest('create_landscape', payload, {
49
+ timeoutMs: 60000
269
50
  });
270
- if (!interpreted.success) {
51
+ if (response.success === false) {
271
52
  return {
272
53
  success: false,
273
- error: interpreted.error || interpreted.message
54
+ error: response.error || response.message || 'Failed to create landscape actor'
274
55
  };
275
56
  }
276
57
  const result = {
277
58
  success: true,
278
- message: interpreted.message,
279
- landscapeName: coerceString(interpreted.payload.landscapeName) ?? name,
280
- worldPartition: coerceBoolean(interpreted.payload.worldPartition)
59
+ message: response.message || 'Landscape actor created',
60
+ landscapeName: response.landscapeName || name,
61
+ worldPartition: response.worldPartition ?? params.enableWorldPartition ?? false
281
62
  };
282
- const actorPath = coerceString(interpreted.payload.landscapeActor);
283
- if (actorPath) {
284
- result.landscapeActor = actorPath;
63
+ if (response.landscapeActor) {
64
+ result.landscapeActor = response.landscapeActor;
285
65
  }
286
- if (interpreted.warnings?.length) {
287
- result.warnings = interpreted.warnings;
66
+ if (response.warnings) {
67
+ result.warnings = response.warnings;
288
68
  }
289
- if (interpreted.details?.length) {
290
- result.details = interpreted.details;
69
+ if (response.details) {
70
+ result.details = response.details;
291
71
  }
292
72
  if (params.runtimeGrid) {
293
73
  result.runtimeGrid = params.runtimeGrid;
@@ -300,78 +80,202 @@ print("RESULT:" + json.dumps(result))
300
80
  catch (error) {
301
81
  return {
302
82
  success: false,
303
- error: `Failed to create landscape actor: ${error}`
83
+ error: `Failed to create landscape actor: ${error instanceof Error ? error.message : String(error)}`
304
84
  };
305
85
  }
306
86
  }
307
- // Sculpt landscape
308
- async sculptLandscape(_params) {
309
- return { success: false, error: 'sculptLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
87
+ async sculptLandscape(params) {
88
+ const [x, y, z] = ensureVector3(params.location ?? [0, 0, 0], 'sculpt location');
89
+ const tool = (params.tool || '').trim();
90
+ const lowerTool = tool.toLowerCase();
91
+ const validTools = new Set(['sculpt', 'smooth', 'flatten', 'ramp', 'erosion', 'hydro', 'noise', 'raise', 'lower']);
92
+ const isValidTool = lowerTool.length > 0 && validTools.has(lowerTool);
93
+ if (!isValidTool) {
94
+ return {
95
+ success: false,
96
+ error: `Invalid sculpt tool: ${params.tool}`
97
+ };
98
+ }
99
+ if (!this.automationBridge) {
100
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
101
+ }
102
+ const payload = {
103
+ landscapeName: params.landscapeName?.trim(),
104
+ toolMode: tool,
105
+ brushRadius: params.brushSize ?? params.radius ?? 1000,
106
+ brushFalloff: params.brushFalloff ?? 0.5,
107
+ strength: params.strength ?? 0.1,
108
+ location: { x, y, z }
109
+ };
110
+ const response = await this.automationBridge.sendAutomationRequest('sculpt_landscape', payload);
111
+ if (!response.success) {
112
+ return {
113
+ success: false,
114
+ error: response.error || 'Failed to sculpt landscape'
115
+ };
116
+ }
117
+ return {
118
+ success: true,
119
+ message: `Sculpting applied to ${params.landscapeName}`,
120
+ details: response
121
+ };
310
122
  }
311
- // Paint landscape
312
- async paintLandscape(_params) {
313
- return { success: false, error: 'paintLandscape not implemented via Remote Control. Requires Landscape editor tools.' };
123
+ async paintLandscape(params) {
124
+ if (!this.automationBridge) {
125
+ throw new Error('Automation Bridge not available.');
126
+ }
127
+ const [x, y] = ensureVector3(params.position, 'paint position');
128
+ const radius = params.brushSize ?? params.radius ?? 1000;
129
+ const minX = Math.floor(x - radius);
130
+ const maxX = Math.floor(x + radius);
131
+ const minY = Math.floor(y - radius);
132
+ const maxY = Math.floor(y + radius);
133
+ const payload = {
134
+ landscapeName: params.landscapeName?.trim(),
135
+ layerName: params.layerName?.trim(),
136
+ region: { minX, minY, maxX, maxY },
137
+ strength: params.strength ?? 1.0
138
+ };
139
+ const response = await this.automationBridge.sendAutomationRequest('paint_landscape_layer', payload);
140
+ if (!response.success) {
141
+ return {
142
+ success: false,
143
+ error: response.error || 'Failed to paint landscape layer'
144
+ };
145
+ }
146
+ return {
147
+ success: true,
148
+ message: `Painted layer ${params.layerName}`,
149
+ details: response
150
+ };
314
151
  }
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}`);
152
+ async createProceduralTerrain(params) {
153
+ if (!this.automationBridge) {
154
+ throw new Error('Automation Bridge not available. Procedural terrain creation requires plugin support.');
321
155
  }
322
- if (params.blendMode) {
323
- commands.push(`SetLayerBlendMode ${params.layerName} ${params.blendMode}`);
156
+ try {
157
+ const payload = {
158
+ name: params.name,
159
+ location: params.location || [0, 0, 0],
160
+ sizeX: params.sizeX || 2000,
161
+ sizeY: params.sizeY || 2000,
162
+ subdivisions: params.subdivisions || 50,
163
+ heightFunction: params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30',
164
+ material: params.material,
165
+ ...params.settings
166
+ };
167
+ const response = await this.automationBridge.sendAutomationRequest('create_procedural_terrain', payload, {
168
+ timeoutMs: 120000
169
+ });
170
+ if (response.success === false) {
171
+ return {
172
+ success: false,
173
+ error: response.error || response.message || 'Failed to create procedural terrain',
174
+ message: response.message || 'Failed to create procedural terrain'
175
+ };
176
+ }
177
+ const result = response.result;
178
+ return {
179
+ success: true,
180
+ message: response.message || `Created procedural terrain '${params.name}'`,
181
+ actorName: result?.actor_name,
182
+ vertices: result?.vertices,
183
+ triangles: result?.triangles,
184
+ size: result?.size,
185
+ subdivisions: result?.subdivisions,
186
+ details: result
187
+ };
188
+ }
189
+ catch (error) {
190
+ return {
191
+ success: false,
192
+ error: `Failed to create procedural terrain: ${error instanceof Error ? error.message : String(error)}`
193
+ };
324
194
  }
325
- await this.bridge.executeConsoleCommands(commands);
326
- return { success: true, message: `Layer ${params.layerName} added to landscape` };
327
195
  }
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(' ')}`);
196
+ async createLandscapeGrassType(params) {
197
+ if (!this.automationBridge) {
198
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
334
199
  }
335
- if (params.width !== undefined) {
336
- commands.push(`SetSplineWidth ${params.splineName} ${params.width}`);
200
+ const name = typeof params.name === 'string' ? params.name.trim() : '';
201
+ if (!name) {
202
+ return { success: false, error: 'Grass type name is required' };
337
203
  }
338
- if (params.falloffWidth !== undefined) {
339
- commands.push(`SetSplineFalloffWidth ${params.splineName} ${params.falloffWidth}`);
204
+ const meshPathRaw = typeof params.meshPath === 'string' && params.meshPath.trim().length > 0
205
+ ? params.meshPath.trim()
206
+ : (typeof params.path === 'string' && params.path.trim().length > 0
207
+ ? params.path.trim()
208
+ : (typeof params.staticMesh === 'string' && params.staticMesh.trim().length > 0
209
+ ? params.staticMesh.trim()
210
+ : ''));
211
+ if (!meshPathRaw) {
212
+ return { success: false, error: 'meshPath is required to create a landscape grass type' };
340
213
  }
341
- if (params.meshPath) {
342
- commands.push(`SetSplineMesh ${params.splineName} ${params.meshPath}`);
214
+ try {
215
+ const response = await this.automationBridge.sendAutomationRequest('create_landscape_grass_type', {
216
+ name,
217
+ meshPath: meshPathRaw,
218
+ density: params.density || 1.0,
219
+ minScale: params.minScale || 0.8,
220
+ maxScale: params.maxScale || 1.2
221
+ }, { timeoutMs: 90000 });
222
+ if (response && response.success === false) {
223
+ return {
224
+ success: false,
225
+ error: response.error || response.message || 'Failed to create landscape grass type'
226
+ };
227
+ }
228
+ const result = response.result;
229
+ return {
230
+ success: true,
231
+ message: response?.message || `Landscape grass type '${name}' created`,
232
+ assetPath: result?.asset_path || response?.assetPath || response?.asset_path
233
+ };
234
+ }
235
+ catch (error) {
236
+ return {
237
+ success: false,
238
+ error: `Failed to create landscape grass type: ${error instanceof Error ? error.message : String(error)}`
239
+ };
343
240
  }
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
241
  }
359
- // Set landscape LOD
360
- async setLandscapeLOD(params) {
361
- const commands = [];
362
- if (params.lodBias !== undefined) {
363
- commands.push(`SetLandscapeLODBias ${params.landscapeName} ${params.lodBias}`);
242
+ async setLandscapeMaterial(params) {
243
+ const landscapeName = typeof params.landscapeName === 'string' ? params.landscapeName.trim() : '';
244
+ const materialPath = typeof params.materialPath === 'string' ? params.materialPath.trim() : '';
245
+ if (!landscapeName) {
246
+ return { success: false, error: 'Landscape name is required' };
364
247
  }
365
- if (params.forcedLOD !== undefined) {
366
- commands.push(`SetLandscapeForcedLOD ${params.landscapeName} ${params.forcedLOD}`);
248
+ if (!materialPath) {
249
+ return { success: false, error: 'materialPath is required' };
367
250
  }
368
- if (params.lodDistribution !== undefined) {
369
- commands.push(`SetLandscapeLODDistribution ${params.landscapeName} ${params.lodDistribution}`);
251
+ if (!this.automationBridge) {
252
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
253
+ }
254
+ try {
255
+ const response = await this.automationBridge.sendAutomationRequest('set_landscape_material', {
256
+ landscapeName,
257
+ materialPath
258
+ }, { timeoutMs: 60000 });
259
+ if (response && response.success === false) {
260
+ return {
261
+ success: false,
262
+ error: response.error || response.message || 'Failed to set landscape material'
263
+ };
264
+ }
265
+ return {
266
+ success: true,
267
+ message: response?.message || `Landscape material set on '${landscapeName}'`,
268
+ landscapeName: response?.landscapeName || landscapeName,
269
+ materialPath: response?.materialPath || materialPath
270
+ };
271
+ }
272
+ catch (error) {
273
+ return {
274
+ success: false,
275
+ error: `Failed to set landscape material: ${error instanceof Error ? error.message : String(error)}`
276
+ };
370
277
  }
371
- await this.bridge.executeConsoleCommands(commands);
372
- return { success: true, message: 'Landscape LOD settings updated' };
373
278
  }
374
- // Create landscape grass
375
279
  async createLandscapeGrass(params) {
376
280
  const commands = [];
377
281
  commands.push(`CreateLandscapeGrass ${params.landscapeName} ${params.grassType}`);
@@ -387,7 +291,6 @@ print("RESULT:" + json.dumps(result))
387
291
  await this.bridge.executeConsoleCommands(commands);
388
292
  return { success: true, message: `Grass type ${params.grassType} created on landscape` };
389
293
  }
390
- // Landscape collision
391
294
  async updateLandscapeCollision(params) {
392
295
  const commands = [];
393
296
  if (params.collisionMipLevel !== undefined) {
@@ -400,7 +303,6 @@ print("RESULT:" + json.dumps(result))
400
303
  await this.bridge.executeConsoleCommands(commands);
401
304
  return { success: true, message: 'Landscape collision updated' };
402
305
  }
403
- // Retopologize landscape
404
306
  async retopologizeLandscape(params) {
405
307
  const commands = [];
406
308
  if (params.targetTriangleCount !== undefined) {
@@ -413,7 +315,6 @@ print("RESULT:" + json.dumps(result))
413
315
  await this.bridge.executeConsoleCommands(commands);
414
316
  return { success: true, message: 'Landscape retopologized' };
415
317
  }
416
- // Create water body
417
318
  async createWaterBody(params) {
418
319
  const loc = params.location || [0, 0, 0];
419
320
  const size = params.size || [1000, 1000];
@@ -421,111 +322,39 @@ print("RESULT:" + json.dumps(result))
421
322
  const command = `CreateWaterBody ${params.type} ${params.name} ${loc.join(' ')} ${size.join(' ')} ${depth}`;
422
323
  return this.bridge.executeConsoleCommand(command);
423
324
  }
424
- // World Partition support for landscapes (UE 5.6)
425
325
  async configureWorldPartition(params) {
326
+ if (!this.automationBridge) {
327
+ throw new Error('Automation Bridge not available. World Partition operations require plugin support.');
328
+ }
426
329
  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'
330
+ const response = await this.automationBridge.sendAutomationRequest('configure_landscape_world_partition', {
331
+ landscapeName: params.landscapeName,
332
+ enableSpatialLoading: params.enableSpatialLoading,
333
+ runtimeGrid: params.runtimeGrid || '',
334
+ dataLayers: params.dataLayers || [],
335
+ streamingDistance: params.streamingDistance
336
+ }, {
337
+ timeoutMs: 60000
510
338
  });
511
- if (interpreted.success) {
512
- return interpreted.payload;
339
+ if (response.success === false) {
340
+ return {
341
+ success: false,
342
+ error: response.error || response.message || 'World Partition configuration failed'
343
+ };
513
344
  }
514
345
  return {
515
- success: false,
516
- error: interpreted.error ?? 'World Partition configuration failed',
517
- details: bestEffortInterpretedText(interpreted)
346
+ success: true,
347
+ message: response.message || 'World Partition configured',
348
+ changes: response.changes
518
349
  };
519
350
  }
520
351
  catch (err) {
521
- return { success: false, error: `Failed to configure World Partition: ${err}` };
352
+ return { success: false, error: `Failed to configure World Partition: ${err instanceof Error ? err.message : String(err)}` };
522
353
  }
523
354
  }
524
- // Set landscape data layers (UE 5.6)
525
355
  async setDataLayers(params) {
526
356
  try {
527
357
  const commands = [];
528
- // Use console commands for data layer management
529
358
  if (params.operation === 'set' || params.operation === 'add') {
530
359
  for (const layerName of params.dataLayerNames) {
531
360
  commands.push(`wp.Runtime.SetDataLayerRuntimeState Loaded ${layerName}`);
@@ -536,7 +365,6 @@ print('RESULT:' + json.dumps(result))
536
365
  commands.push(`wp.Runtime.SetDataLayerRuntimeState Unloaded ${layerName}`);
537
366
  }
538
367
  }
539
- // Execute commands
540
368
  await this.bridge.executeConsoleCommands(commands);
541
369
  return {
542
370
  success: true,
@@ -548,18 +376,15 @@ print('RESULT:' + json.dumps(result))
548
376
  return { success: false, error: `Failed to manage data layers: ${err}` };
549
377
  }
550
378
  }
551
- // Configure landscape streaming cells (UE 5.6 World Partition)
552
379
  async configureStreamingCells(params) {
553
380
  const commands = [];
554
- // World Partition runtime commands
555
381
  if (params.loadingRange !== undefined) {
556
382
  commands.push(`wp.Runtime.OverrideRuntimeSpatialHashLoadingRange -grid=0 -range=${params.loadingRange}`);
557
383
  }
558
384
  if (params.enableHLOD !== undefined) {
559
385
  commands.push(`wp.Runtime.HLOD ${params.enableHLOD ? '1' : '0'}`);
560
386
  }
561
- // Debug visualization commands
562
- commands.push('wp.Runtime.ToggleDrawRuntimeHash2D'); // Show 2D grid
387
+ commands.push('wp.Runtime.ToggleDrawRuntimeHash2D');
563
388
  try {
564
389
  await this.bridge.executeConsoleCommands(commands);
565
390
  return {
@@ -576,5 +401,51 @@ print('RESULT:' + json.dumps(result))
576
401
  return { success: false, error: `Failed to configure streaming cells: ${err}` };
577
402
  }
578
403
  }
404
+ async modifyHeightmap(params) {
405
+ if (!this.automationBridge) {
406
+ throw new Error('Automation Bridge not available. Landscape operations require plugin support.');
407
+ }
408
+ const { landscapeName, heightData, minX, minY, maxX, maxY } = params;
409
+ if (!landscapeName) {
410
+ return { success: false, error: 'Landscape name is required' };
411
+ }
412
+ if (!heightData || !Array.isArray(heightData) || heightData.length === 0) {
413
+ return { success: false, error: 'heightData array is required' };
414
+ }
415
+ const width = maxX - minX + 1;
416
+ const height = maxY - minY + 1;
417
+ if (heightData.length !== width * height) {
418
+ return {
419
+ success: false,
420
+ error: `Height data length (${heightData.length}) does not match region dimensions (${width}x${height} = ${width * height})`
421
+ };
422
+ }
423
+ try {
424
+ const response = await this.automationBridge.sendAutomationRequest('modify_heightmap', {
425
+ landscapeName,
426
+ heightData,
427
+ minX,
428
+ minY,
429
+ maxX,
430
+ maxY,
431
+ updateNormals: params.updateNormals ?? true
432
+ }, {
433
+ timeoutMs: 60000
434
+ });
435
+ if (response.success === false) {
436
+ return {
437
+ success: false,
438
+ error: response.error || response.message || 'Failed to modify heightmap'
439
+ };
440
+ }
441
+ return {
442
+ success: true,
443
+ message: response.message || 'Heightmap modified successfully'
444
+ };
445
+ }
446
+ catch (err) {
447
+ return { success: false, error: `Failed to modify heightmap: ${err instanceof Error ? err.message : String(err)}` };
448
+ }
449
+ }
579
450
  }
580
451
  //# sourceMappingURL=landscape.js.map