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,732 +0,0 @@
1
- import { UnrealBridge } from '../unreal-bridge.js';
2
- import { interpretStandardResult, coerceBoolean, coerceNumber, coerceString, coerceStringArray } from '../utils/result-helpers.js';
3
-
4
- /**
5
- * Advanced Build Environment Tools
6
- * Implements procedural terrain and foliage using documented Unreal Engine Python APIs
7
- */
8
- export class BuildEnvironmentAdvanced {
9
- constructor(private bridge: UnrealBridge) {}
10
-
11
- /**
12
- * Create procedural terrain using ProceduralMeshComponent
13
- * This works around the landscape API limitations
14
- */
15
- async createProceduralTerrain(params: {
16
- name: string;
17
- location?: [number, number, number];
18
- sizeX?: number;
19
- sizeY?: number;
20
- subdivisions?: number;
21
- heightFunction?: string; // Python expression for height calculation
22
- material?: string;
23
- }) {
24
- const pythonScript = `
25
- import unreal
26
- import json
27
- import math
28
-
29
- name = ${JSON.stringify(params.name)}
30
- location = unreal.Vector(${params.location?.[0] || 0}, ${params.location?.[1] || 0}, ${params.location?.[2] || 0})
31
- size_x = ${params.sizeX || 2000}
32
- size_y = ${params.sizeY || 2000}
33
- subdivisions = ${params.subdivisions || 50}
34
- height_function = ${JSON.stringify(params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30')}
35
-
36
- result = {}
37
-
38
- try:
39
- # Get editor subsystem
40
- subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
41
-
42
- # Create ProceduralMeshActor
43
- proc_actor = None
44
- proc_mesh_comp = None
45
-
46
- # Try ProceduralMeshActor first
47
- try:
48
- proc_actor = subsys.spawn_actor_from_class(
49
- unreal.ProceduralMeshActor,
50
- location,
51
- unreal.Rotator(0, 0, 0)
52
- )
53
- proc_actor.set_actor_label(f"{name}_ProceduralTerrain")
54
- proc_mesh_comp = proc_actor.get_component_by_class(unreal.ProceduralMeshComponent)
55
- except:
56
- # Fallback: Create empty actor and add ProceduralMeshComponent
57
- # If spawning ProceduralMeshActor failed, surface a clear error about the plugin requirement
58
- raise Exception("Failed to spawn ProceduralMeshActor. Ensure the 'Procedural Mesh Component' plugin is enabled and available.")
59
-
60
- if proc_mesh_comp:
61
- # Generate terrain mesh
62
- vertices = []
63
- triangles = []
64
- normals = []
65
- uvs = []
66
- vertex_colors = []
67
-
68
- step_x = size_x / subdivisions
69
- step_y = size_y / subdivisions
70
-
71
- # Create vertices with height variation
72
- for y in range(subdivisions + 1):
73
- for x in range(subdivisions + 1):
74
- # Position
75
- vert_x = x * step_x - size_x / 2
76
- vert_y = y * step_y - size_y / 2
77
-
78
- # Calculate height using the provided function
79
- try:
80
- vert_z = eval(height_function, {"x": vert_x, "y": vert_y, "math": math})
81
- except:
82
- vert_z = 0 # Fallback to flat if function fails
83
-
84
- vertices.append(unreal.Vector(vert_x, vert_y, vert_z))
85
- normals.append(unreal.Vector(0, 0, 1)) # Will be recalculated
86
- uvs.append(unreal.Vector2D(x / subdivisions, y / subdivisions))
87
-
88
- # Color based on height
89
- height_normalized = min(1.0, max(0.0, (vert_z + 100) / 200))
90
- vertex_colors.append(unreal.LinearColor(height_normalized, 1 - height_normalized, 0.2, 1))
91
-
92
- # Create triangles
93
- for y in range(subdivisions):
94
- for x in range(subdivisions):
95
- idx = y * (subdivisions + 1) + x
96
-
97
- # First triangle
98
- triangles.extend([idx, idx + subdivisions + 1, idx + 1])
99
- # Second triangle
100
- triangles.extend([idx + 1, idx + subdivisions + 1, idx + subdivisions + 2])
101
-
102
- # Create mesh section
103
- proc_mesh_comp.create_mesh_section_linear_color(
104
- 0, # Section index
105
- vertices,
106
- triangles,
107
- normals,
108
- uvs,
109
- vertex_colors,
110
- [], # Tangents
111
- True # Create collision
112
- )
113
-
114
- # Apply material if specified
115
- if ${JSON.stringify(params.material || '')}:
116
- material = unreal.EditorAssetLibrary.load_asset(${JSON.stringify(params.material || '/Engine/MapTemplates/Materials/BasicGrid01')})
117
- if material:
118
- proc_mesh_comp.set_material(0, material)
119
-
120
- # Enable collision
121
- proc_mesh_comp.set_collision_enabled(unreal.CollisionEnabled.QUERY_AND_PHYSICS)
122
-
123
- result = {
124
- "success": True,
125
- "message": f"Created procedural terrain '{name}'",
126
- "actor_name": proc_actor.get_actor_label(),
127
- "vertices": len(vertices),
128
- "triangles": len(triangles) // 3,
129
- "size": [size_x, size_y],
130
- "subdivisions": subdivisions
131
- }
132
- else:
133
- result = {"success": False, "error": "Could not create ProceduralMeshComponent"}
134
-
135
- except Exception as e:
136
- result = {"success": False, "error": str(e)}
137
-
138
- print(f"RESULT:{json.dumps(result)}")
139
- `.trim();
140
-
141
- const response = await this.bridge.executePython(pythonScript);
142
- const interpreted = interpretStandardResult(response, {
143
- successMessage: `Created procedural terrain '${params.name}'`,
144
- failureMessage: `Failed to create procedural terrain '${params.name}'`
145
- });
146
-
147
- if (!interpreted.success) {
148
- const failure: {
149
- success: false;
150
- error: string;
151
- message: string;
152
- warnings?: string[];
153
- details?: string[];
154
- payload?: Record<string, unknown>;
155
- } = {
156
- success: false,
157
- error: interpreted.error ?? interpreted.message,
158
- message: interpreted.message
159
- };
160
-
161
- if (interpreted.warnings) {
162
- failure.warnings = interpreted.warnings;
163
- }
164
- if (interpreted.details) {
165
- failure.details = interpreted.details;
166
- }
167
- if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
168
- failure.payload = interpreted.payload;
169
- }
170
-
171
- return failure;
172
- }
173
-
174
- const payload = { ...interpreted.payload } as Record<string, unknown>;
175
- const actorName = coerceString(payload.actor_name) ?? coerceString(payload.actorName);
176
- const vertices = coerceNumber(payload.vertices);
177
- const triangles = coerceNumber(payload.triangles);
178
- const subdivisions = coerceNumber(payload.subdivisions);
179
- const sizeArray = Array.isArray(payload.size)
180
- ? (payload.size as unknown[]).map(entry => {
181
- if (typeof entry === 'number' && Number.isFinite(entry)) {
182
- return entry;
183
- }
184
- if (typeof entry === 'string') {
185
- const parsed = Number(entry);
186
- return Number.isFinite(parsed) ? parsed : undefined;
187
- }
188
- return undefined;
189
- }).filter((entry): entry is number => typeof entry === 'number')
190
- : undefined;
191
-
192
- payload.success = true;
193
- payload.message = interpreted.message;
194
-
195
- if (actorName) {
196
- payload.actor_name = actorName;
197
- payload.actorName = actorName;
198
- }
199
- if (typeof vertices === 'number') {
200
- payload.vertices = vertices;
201
- }
202
- if (typeof triangles === 'number') {
203
- payload.triangles = triangles;
204
- }
205
- if (typeof subdivisions === 'number') {
206
- payload.subdivisions = subdivisions;
207
- }
208
- if (sizeArray && sizeArray.length === 2) {
209
- payload.size = sizeArray;
210
- }
211
-
212
- if (interpreted.warnings) {
213
- payload.warnings = interpreted.warnings;
214
- }
215
- if (interpreted.details) {
216
- payload.details = interpreted.details;
217
- }
218
-
219
- return payload as any;
220
- }
221
-
222
- /**
223
- * Create procedural foliage using ProceduralFoliageSpawner
224
- * Uses the documented Unreal Engine API
225
- */
226
- async createProceduralFoliage(params: {
227
- name: string;
228
- bounds: { location: [number, number, number]; size: [number, number, number] };
229
- foliageTypes: Array<{
230
- meshPath: string;
231
- density: number;
232
- minScale?: number;
233
- maxScale?: number;
234
- alignToNormal?: boolean;
235
- randomYaw?: boolean;
236
- }>;
237
- seed?: number;
238
- }) {
239
- const pythonScript = `
240
- import unreal
241
- import json
242
-
243
- name = ${JSON.stringify(params.name)}
244
- bounds_location = unreal.Vector(${params.bounds.location[0]}, ${params.bounds.location[1]}, ${params.bounds.location[2]})
245
- bounds_size = unreal.Vector(${params.bounds.size[0]}, ${params.bounds.size[1]}, ${params.bounds.size[2]})
246
- seed = ${params.seed || 12345}
247
-
248
- result = {}
249
-
250
- try:
251
- subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
252
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
253
-
254
- # Validate Procedural Foliage plugin/classes are available
255
- if not hasattr(unreal, 'ProceduralFoliageVolume') or not hasattr(unreal, 'ProceduralFoliageSpawner'):
256
- raise Exception("Procedural Foliage plugin not available. Please enable the 'Procedural Foliage' plugin and try again.")
257
-
258
- # Create ProceduralFoliageVolume
259
- volume_actor = subsys.spawn_actor_from_class(
260
- unreal.ProceduralFoliageVolume,
261
- bounds_location,
262
- unreal.Rotator(0, 0, 0)
263
- )
264
- volume_actor.set_actor_label(f"{name}_ProceduralFoliageVolume")
265
- volume_actor.set_actor_scale3d(unreal.Vector(bounds_size.x/100.0, bounds_size.y/100.0, bounds_size.z/100.0)) # Scale is in meters
266
-
267
- # Get the procedural component
268
- proc_comp = volume_actor.procedural_component
269
- if not proc_comp:
270
- proc_comp = volume_actor.get_component_by_class(unreal.ProceduralFoliageComponent)
271
-
272
- if proc_comp:
273
- # Create ProceduralFoliageSpawner asset
274
- spawner_path = f"/Game/Foliage/Spawners/{name}_Spawner"
275
- package_path = "/Game/Foliage/Spawners"
276
-
277
- # Ensure directory exists
278
- if not unreal.EditorAssetLibrary.does_directory_exist(package_path):
279
- unreal.EditorAssetLibrary.make_directory(package_path)
280
-
281
- # Create spawner
282
- spawner = None
283
- if unreal.EditorAssetLibrary.does_asset_exist(spawner_path):
284
- spawner = unreal.EditorAssetLibrary.load_asset(spawner_path)
285
- else:
286
- # Create new spawner
287
- factory = unreal.ProceduralFoliageSpawnerFactory()
288
- spawner = asset_tools.create_asset(
289
- asset_name=f"{name}_Spawner",
290
- package_path=package_path,
291
- asset_class=unreal.ProceduralFoliageSpawner,
292
- factory=factory
293
- )
294
-
295
- if spawner:
296
- # Configure spawner (use set_editor_property for read-only attributes)
297
- spawner.set_editor_property('random_seed', seed)
298
- spawner.set_editor_property('tile_size', max(bounds_size.x, bounds_size.y))
299
-
300
- # Create foliage types
301
- foliage_types = []
302
- ft_input = json.loads(r'''${JSON.stringify(params.foliageTypes)}''')
303
- for ft_params in ft_input:
304
- # Load mesh
305
- mesh = unreal.EditorAssetLibrary.load_asset(ft_params['meshPath'])
306
- if mesh:
307
- # Create FoliageTypeObject
308
- ft_obj = unreal.FoliageTypeObject()
309
-
310
- # Try to create or load FoliageType_InstancedStaticMesh
311
- ft_asset_name = f"FT_{name}_{len(foliage_types)}"
312
- ft_asset_path = f"/Game/Foliage/Types/{ft_asset_name}"
313
-
314
- ft_asset = None
315
- if unreal.EditorAssetLibrary.does_asset_exist(ft_asset_path):
316
- ft_asset = unreal.EditorAssetLibrary.load_asset(ft_asset_path)
317
- else:
318
- # Create simple foliage type
319
- ft_asset = unreal.FoliageType_InstancedStaticMesh()
320
-
321
- if ft_asset:
322
- # Configure foliage type (use set_editor_property)
323
- ft_asset.set_editor_property('mesh', mesh)
324
- ft_asset.set_editor_property('density', ft_params.get('density', 1.0))
325
- ft_asset.set_editor_property('random_yaw', ft_params.get('randomYaw', True))
326
- ft_asset.set_editor_property('align_to_normal', ft_params.get('alignToNormal', True))
327
-
328
- min_scale = ft_params.get('minScale', 0.8)
329
- max_scale = ft_params.get('maxScale', 1.2)
330
- ft_asset.set_editor_property('scale_x', unreal.FloatInterval(min_scale, max_scale))
331
- ft_asset.set_editor_property('scale_y', unreal.FloatInterval(min_scale, max_scale))
332
- ft_asset.set_editor_property('scale_z', unreal.FloatInterval(min_scale, max_scale))
333
-
334
- ft_obj.set_editor_property('foliage_type_object', ft_asset)
335
- foliage_types.append(ft_obj)
336
-
337
- # Set foliage types on spawner
338
- spawner.set_editor_property('foliage_types', foliage_types)
339
-
340
- # Assign spawner to component
341
- proc_comp.set_editor_property('foliage_spawner', spawner)
342
-
343
- # Save spawner asset
344
- unreal.EditorAssetLibrary.save_asset(spawner.get_path_name())
345
-
346
- # Resimulate
347
- try:
348
- unreal.ProceduralFoliageEditorLibrary.resimulate_procedural_foliage_volumes([volume_actor])
349
- result['resimulated'] = True
350
- except:
351
- # Manual simulation
352
- spawner.simulate(num_steps=-1)
353
- result['resimulated'] = False
354
- result['note'] = 'Used manual simulation'
355
-
356
- result['success'] = True
357
- result['message'] = f"Created procedural foliage volume '{name}'"
358
- result['volume_actor'] = volume_actor.get_actor_label()
359
- result['spawner_path'] = spawner.get_path_name()
360
- result['foliage_types_count'] = len(foliage_types)
361
- else:
362
- result['success'] = False
363
- result['error'] = 'Could not create ProceduralFoliageSpawner'
364
- else:
365
- result['success'] = False
366
- result['error'] = 'Could not get ProceduralFoliageComponent'
367
-
368
- except Exception as e:
369
- result['success'] = False
370
- result['error'] = str(e)
371
-
372
- print(f"RESULT:{json.dumps(result)}")
373
- `.trim();
374
-
375
- const response = await this.bridge.executePython(pythonScript);
376
- const interpreted = interpretStandardResult(response, {
377
- successMessage: `Created procedural foliage volume '${params.name}'`,
378
- failureMessage: `Failed to create procedural foliage volume '${params.name}'`
379
- });
380
-
381
- if (!interpreted.success) {
382
- const failure: {
383
- success: false;
384
- error: string;
385
- message: string;
386
- warnings?: string[];
387
- details?: string[];
388
- payload?: Record<string, unknown>;
389
- } = {
390
- success: false,
391
- error: interpreted.error ?? interpreted.message,
392
- message: interpreted.message
393
- };
394
-
395
- if (interpreted.warnings) {
396
- failure.warnings = interpreted.warnings;
397
- }
398
- if (interpreted.details) {
399
- failure.details = interpreted.details;
400
- }
401
- if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
402
- failure.payload = interpreted.payload;
403
- }
404
-
405
- return failure;
406
- }
407
-
408
- const payload = { ...interpreted.payload } as Record<string, unknown>;
409
- const volumeActor = coerceString(payload.volume_actor) ?? coerceString(payload.volumeActor);
410
- const spawnerPath = coerceString(payload.spawner_path) ?? coerceString(payload.spawnerPath);
411
- const foliageCount = coerceNumber(payload.foliage_types_count) ?? coerceNumber(payload.foliageTypesCount);
412
- const resimulated = coerceBoolean(payload.resimulated);
413
- const note = coerceString(payload.note);
414
- const messages = coerceStringArray(payload.messages);
415
-
416
- payload.success = true;
417
- payload.message = interpreted.message;
418
-
419
- if (volumeActor) {
420
- payload.volume_actor = volumeActor;
421
- payload.volumeActor = volumeActor;
422
- }
423
- if (spawnerPath) {
424
- payload.spawner_path = spawnerPath;
425
- payload.spawnerPath = spawnerPath;
426
- }
427
- if (typeof foliageCount === 'number') {
428
- payload.foliage_types_count = foliageCount;
429
- payload.foliageTypesCount = foliageCount;
430
- }
431
- if (typeof resimulated === 'boolean') {
432
- payload.resimulated = resimulated;
433
- }
434
- if (note) {
435
- payload.note = note;
436
- }
437
- if (messages && messages.length > 0) {
438
- payload.messages = messages;
439
- }
440
-
441
- if (interpreted.warnings) {
442
- payload.warnings = interpreted.warnings;
443
- }
444
- if (interpreted.details) {
445
- payload.details = interpreted.details;
446
- }
447
-
448
- return payload as any;
449
- }
450
-
451
- /**
452
- * Add foliage instances using InstancedFoliageActor
453
- * Direct instance placement approach
454
- */
455
- async addFoliageInstances(params: {
456
- foliageType: string; // Path to FoliageType or mesh
457
- transforms: Array<{
458
- location: [number, number, number];
459
- rotation?: [number, number, number];
460
- scale?: [number, number, number];
461
- }>;
462
- }) {
463
- const pythonScript = `
464
- import unreal
465
- import json
466
-
467
- foliage_type_path = ${JSON.stringify(params.foliageType)}
468
- transforms_data = ${JSON.stringify(params.transforms)}
469
-
470
- result = {}
471
-
472
- try:
473
- # Get world context
474
- editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
475
- world = editor_subsystem.get_editor_world()
476
-
477
- # Load foliage type or mesh
478
- foliage_asset = unreal.EditorAssetLibrary.load_asset(foliage_type_path)
479
-
480
- if foliage_asset:
481
- # Prepare transforms
482
- transforms = []
483
- for t_data in transforms_data:
484
- location = unreal.Vector(t_data['location'][0], t_data['location'][1], t_data['location'][2])
485
- rotation = unreal.Rotator(
486
- t_data.get('rotation', [0, 0, 0])[0],
487
- t_data.get('rotation', [0, 0, 0])[1],
488
- t_data.get('rotation', [0, 0, 0])[2]
489
- )
490
- scale = unreal.Vector(
491
- t_data.get('scale', [1, 1, 1])[0],
492
- t_data.get('scale', [1, 1, 1])[1],
493
- t_data.get('scale', [1, 1, 1])[2]
494
- )
495
-
496
- transform = unreal.Transform(location, rotation, scale)
497
- transforms.append(transform)
498
-
499
- # Add instances using InstancedFoliageActor
500
- unreal.InstancedFoliageActor.add_instances(
501
- world,
502
- foliage_asset,
503
- transforms
504
- )
505
-
506
- result['success'] = True
507
- result['message'] = f"Added {len(transforms)} foliage instances"
508
- result['instances_count'] = len(transforms)
509
- else:
510
- result['success'] = False
511
- result['error'] = f"Could not load foliage asset: {foliage_type_path}"
512
-
513
- except Exception as e:
514
- result['success'] = False
515
- result['error'] = str(e)
516
-
517
- print(f"RESULT:{json.dumps(result)}")
518
- `.trim();
519
-
520
- const response = await this.bridge.executePython(pythonScript);
521
- const interpreted = interpretStandardResult(response, {
522
- successMessage: 'Foliage instances added',
523
- failureMessage: 'Failed to add foliage instances'
524
- });
525
-
526
- if (!interpreted.success) {
527
- const failure: {
528
- success: false;
529
- error: string;
530
- message: string;
531
- warnings?: string[];
532
- details?: string[];
533
- payload?: Record<string, unknown>;
534
- } = {
535
- success: false,
536
- error: interpreted.error ?? interpreted.message,
537
- message: interpreted.message
538
- };
539
-
540
- if (interpreted.warnings) {
541
- failure.warnings = interpreted.warnings;
542
- }
543
- if (interpreted.details) {
544
- failure.details = interpreted.details;
545
- }
546
- if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
547
- failure.payload = interpreted.payload;
548
- }
549
-
550
- return failure;
551
- }
552
-
553
- const payload = { ...interpreted.payload } as Record<string, unknown>;
554
- const count = coerceNumber(payload.instances_count) ?? coerceNumber(payload.instancesCount);
555
- const message = coerceString(payload.message) ?? interpreted.message;
556
-
557
- payload.success = true;
558
- payload.message = message;
559
-
560
- if (typeof count === 'number') {
561
- payload.instances_count = count;
562
- payload.instancesCount = count;
563
- }
564
-
565
- if (interpreted.warnings) {
566
- payload.warnings = interpreted.warnings;
567
- }
568
- if (interpreted.details) {
569
- payload.details = interpreted.details;
570
- }
571
-
572
- return payload as any;
573
- }
574
-
575
- /**
576
- * Create landscape grass type for automatic foliage on landscape
577
- */
578
- async createLandscapeGrassType(params: {
579
- name: string;
580
- meshPath: string;
581
- density?: number;
582
- minScale?: number;
583
- maxScale?: number;
584
- }) {
585
- const pythonScript = `
586
- import unreal
587
- import json
588
-
589
- name = ${JSON.stringify(params.name)}
590
- mesh_path = ${JSON.stringify(params.meshPath)}
591
- density = ${params.density || 1.0}
592
- min_scale = ${params.minScale || 0.8}
593
- max_scale = ${params.maxScale || 1.2}
594
-
595
- result = {}
596
-
597
- try:
598
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
599
-
600
- # Create directory
601
- package_path = "/Game/Landscape/GrassTypes"
602
- if not unreal.EditorAssetLibrary.does_directory_exist(package_path):
603
- unreal.EditorAssetLibrary.make_directory(package_path)
604
-
605
- # Create LandscapeGrassType
606
- grass_type_path = f"{package_path}/{name}"
607
-
608
- if not unreal.EditorAssetLibrary.does_asset_exist(grass_type_path):
609
- # Create using factory
610
- factory = unreal.LandscapeGrassTypeFactory()
611
- grass_type = asset_tools.create_asset(
612
- asset_name=name,
613
- package_path=package_path,
614
- asset_class=unreal.LandscapeGrassType,
615
- factory=factory
616
- )
617
-
618
- if grass_type:
619
- # Load mesh
620
- mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
621
- if mesh:
622
- # Configure grass type (use set_editor_property)
623
- grass_variety = unreal.GrassVariety()
624
- grass_variety.set_editor_property('grass_mesh', mesh)
625
- # GrassDensity is PerPlatformFloat in UE5+; set via struct instance
626
- pp_density = unreal.PerPlatformFloat()
627
- pp_density.set_editor_property('Default', float(density * 100.0))
628
- grass_variety.set_editor_property('grass_density', pp_density)
629
- grass_variety.set_editor_property('use_grid', True)
630
- grass_variety.set_editor_property('placement_jitter', 1.0)
631
- # Set cull distances as PerPlatformInt and LOD as int (engine uses mixed types here)
632
- pp_start = unreal.PerPlatformInt()
633
- pp_start.set_editor_property('Default', 10000)
634
- grass_variety.set_editor_property('start_cull_distance', pp_start)
635
- pp_end = unreal.PerPlatformInt()
636
- pp_end.set_editor_property('Default', 20000)
637
- grass_variety.set_editor_property('end_cull_distance', pp_end)
638
- grass_variety.set_editor_property('min_lod', -1)
639
- grass_variety.set_editor_property('scaling', unreal.GrassScaling.UNIFORM)
640
- grass_variety.set_editor_property('scale_x', unreal.FloatInterval(min_scale, max_scale))
641
- grass_variety.set_editor_property('scale_y', unreal.FloatInterval(min_scale, max_scale))
642
- grass_variety.set_editor_property('scale_z', unreal.FloatInterval(min_scale, max_scale))
643
- grass_variety.set_editor_property('random_rotation', True)
644
- grass_variety.set_editor_property('align_to_surface', True)
645
-
646
- grass_type.set_editor_property('grass_varieties', [grass_variety])
647
-
648
- # Save asset
649
- unreal.EditorAssetLibrary.save_asset(grass_type.get_path_name())
650
-
651
- result['success'] = True
652
- result['message'] = f"Created landscape grass type '{name}'"
653
- result['asset_path'] = grass_type.get_path_name()
654
- else:
655
- result['success'] = False
656
- result['error'] = f"Could not load mesh: {mesh_path}"
657
- else:
658
- result['success'] = False
659
- result['error'] = "Could not create LandscapeGrassType"
660
- else:
661
- result['success'] = False
662
- result['error'] = f"Grass type already exists: {grass_type_path}"
663
-
664
- except Exception as e:
665
- result['success'] = False
666
- result['error'] = str(e)
667
-
668
- print(f"RESULT:{json.dumps(result)}")
669
- `.trim();
670
-
671
- const response = await this.bridge.executePython(pythonScript);
672
- const interpreted = interpretStandardResult(response, {
673
- successMessage: `Created landscape grass type '${params.name}'`,
674
- failureMessage: `Failed to create landscape grass type '${params.name}'`
675
- });
676
-
677
- if (!interpreted.success) {
678
- const failure: {
679
- success: false;
680
- error: string;
681
- message: string;
682
- warnings?: string[];
683
- details?: string[];
684
- payload?: Record<string, unknown>;
685
- } = {
686
- success: false,
687
- error: interpreted.error ?? interpreted.message,
688
- message: interpreted.message
689
- };
690
-
691
- if (interpreted.warnings) {
692
- failure.warnings = interpreted.warnings;
693
- }
694
- if (interpreted.details) {
695
- failure.details = interpreted.details;
696
- }
697
- if (interpreted.payload && Object.keys(interpreted.payload).length > 0) {
698
- failure.payload = interpreted.payload;
699
- }
700
-
701
- return failure;
702
- }
703
-
704
- const payload = { ...interpreted.payload } as Record<string, unknown>;
705
- const assetPath = coerceString(payload.asset_path) ?? coerceString(payload.assetPath);
706
- const note = coerceString(payload.note);
707
- const messages = coerceStringArray(payload.messages);
708
-
709
- payload.success = true;
710
- payload.message = interpreted.message;
711
-
712
- if (assetPath) {
713
- payload.asset_path = assetPath;
714
- payload.assetPath = assetPath;
715
- }
716
- if (note) {
717
- payload.note = note;
718
- }
719
- if (messages && messages.length > 0) {
720
- payload.messages = messages;
721
- }
722
-
723
- if (interpreted.warnings) {
724
- payload.warnings = interpreted.warnings;
725
- }
726
- if (interpreted.details) {
727
- payload.details = interpreted.details;
728
- }
729
-
730
- return payload as any;
731
- }
732
- }