@underpostnet/cyberia 3.3.73 → 3.3.77

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 (269) hide show
  1. package/.env.example +17 -5
  2. package/.github/workflows/coverall.cyberia.ci.yml +10 -0
  3. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  4. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  5. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  8. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  9. package/.github/workflows/engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  11. package/AGENTS.md +61 -12
  12. package/CHANGELOG.md +159 -161
  13. package/CLI-HELP.md +20 -13
  14. package/Dockerfile +1 -1
  15. package/Dockerfile.dev +1 -1
  16. package/Dockerfile.test +2 -2
  17. package/README.md +28 -28
  18. package/bin/build.js +96 -21
  19. package/bin/cyberia.js +1247 -1373
  20. package/bin/index.js +1247 -1373
  21. package/compose.env +17 -5
  22. package/conf.js +7 -4
  23. package/deploy/cyberia-client/deploy.sh +10 -11
  24. package/deploy/cyberia-client/package.sh +0 -1
  25. package/deploy/cyberia-client/state.sh +3 -2
  26. package/deploy/cyberia-server/deploy.sh +10 -11
  27. package/deploy/cyberia-server/package.sh +0 -1
  28. package/deploy/cyberia-server/state.sh +3 -2
  29. package/deploy/dd-cyberia/deploy.sh +5 -5
  30. package/deploy/dd-cyberia/init.sh +15 -14
  31. package/deploy/dd-cyberia/package.sh +0 -1
  32. package/deploy/dd-cyberia/state.sh +2 -2
  33. package/deploy/dd-cyberia/sync-deploy.sh +139 -75
  34. package/deploy/lib/config.sh +29 -0
  35. package/deploy/lib/github-actions-logging.sh +92 -76
  36. package/deploy/lib/host.sh +28 -19
  37. package/deploy/lib/state.sh +4 -1
  38. package/deploy/pwa-microservices-template/deploy.sh +10 -8
  39. package/deploy/release/deploy.sh +0 -2
  40. package/deployment.yaml +1 -1
  41. package/docker-compose.yml +23 -17
  42. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  43. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  44. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  45. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  46. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  47. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  48. package/docs/coverage/cyberia/base.css +362 -0
  49. package/docs/coverage/cyberia/block-navigation.js +82 -0
  50. package/docs/coverage/cyberia/favicon.png +0 -0
  51. package/docs/coverage/cyberia/index.html +161 -0
  52. package/docs/coverage/cyberia/prettify.css +101 -0
  53. package/docs/coverage/cyberia/prettify.js +937 -0
  54. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  55. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  56. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  57. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  58. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  59. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  60. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  61. package/docs/coverage/cyberia/sorter.js +205 -0
  62. package/hardhat/package-lock.json +2 -2
  63. package/hardhat/package.json +1 -1
  64. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  65. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  66. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  67. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  68. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  69. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  70. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  71. package/manifests/deployment/mongo-express/service.yaml +16 -0
  72. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  73. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  74. package/package.json +19 -17
  75. package/scripts/test-monitor.sh +3 -3
  76. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
  77. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  78. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  79. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
  80. package/src/api/cyberia-action/cyberia-action.router.js +2 -2
  81. package/src/api/cyberia-action/cyberia-action.service.js +3 -4
  82. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  83. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  84. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  85. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  86. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
  87. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  88. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
  89. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
  90. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  91. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  92. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  93. package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
  94. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  95. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  96. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
  97. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
  98. package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
  99. package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
  100. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  101. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  102. package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
  103. package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
  104. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  105. package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
  106. package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
  107. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  108. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  109. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
  110. package/src/api/cyberia-map/cyberia-map.service.js +6 -2
  111. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  112. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  113. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  114. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  115. package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
  116. package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
  117. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  118. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
  119. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  120. package/src/api/file/file.ref.js +153 -0
  121. package/src/api/file/file.ref.json +11 -2
  122. package/src/api/object-layer/object-layer.model.js +47 -12
  123. package/src/api/object-layer/object-layer.router.js +10 -3
  124. package/src/api/object-layer/object-layer.service.js +113 -120
  125. package/src/cli/app.js +12 -1
  126. package/src/cli/client.js +2 -2
  127. package/src/cli/cluster.js +80 -18
  128. package/src/cli/deploy.js +215 -34
  129. package/src/cli/docker-compose.js +1 -1
  130. package/src/cli/fs-selection.js +115 -0
  131. package/src/cli/fs.js +158 -342
  132. package/src/cli/index.js +22 -10
  133. package/src/cli/release.js +3 -1
  134. package/src/cli/repository.js +35 -9
  135. package/src/cli/run.js +80 -199
  136. package/src/cli/test.js +4 -0
  137. package/src/cli/wireguard.js +32 -31
  138. package/src/client/components/core/Badge.js +0 -1
  139. package/src/client/components/core/CssCore.js +8 -1
  140. package/src/client/components/core/Docs.js +77 -100
  141. package/src/client/components/core/Modal.js +77 -23
  142. package/src/client/components/core/ToolTip.js +15 -0
  143. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  144. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  145. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  146. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  147. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  148. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  149. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  150. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  151. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  152. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  153. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
  154. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  155. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  156. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  157. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  158. package/src/client/components/default/AppShellDefault.js +1 -4
  159. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  160. package/src/client/components/underpost/AppShellUnderpost.js +1 -1
  161. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  162. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
  163. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  164. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  165. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  166. package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
  167. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  168. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  169. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  170. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  171. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  172. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  173. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  174. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  175. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  176. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  177. package/src/client/services/object-layer/object-layer.management.js +6 -10
  178. package/src/client/services/object-layer/object-layer.service.js +3 -1
  179. package/src/client/ssr/views/Cyberia404.js +3 -3
  180. package/src/client-builder/client-build-docs.js +52 -52
  181. package/src/client-builder/client-build.js +67 -44
  182. package/src/client-builder/client-bundle.js +347 -0
  183. package/src/db/mongo/MongoExpress.js +187 -0
  184. package/src/grpc/cyberia/grpc-server.js +5 -7
  185. package/src/index.js +1 -1
  186. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
  187. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  188. package/src/projects/cyberia/besu-genesis-generator.js +5 -14
  189. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  190. package/src/projects/cyberia/catalog-cyberia.js +3 -2
  191. package/src/projects/cyberia/gemini-client.js +3 -3
  192. package/src/projects/cyberia/generate-saga.js +4 -11
  193. package/src/projects/cyberia/hot-reload-trigger.js +17 -28
  194. package/src/projects/cyberia/instance-backup.js +210 -0
  195. package/src/projects/cyberia/instance-data.js +177 -257
  196. package/src/projects/cyberia/map-preview-generator.js +32 -68
  197. package/src/projects/cyberia/object-layer.js +152 -319
  198. package/src/projects/cyberia/seed-audio.js +139 -0
  199. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  200. package/src/projects/cyberia/semantic-layer-generator.js +2 -18
  201. package/src/projects/cyberia/stat-balance.js +81 -0
  202. package/src/projects/cyberia/stat-commands.js +23 -0
  203. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  204. package/src/runtime/cyberia-client/Dockerfile +8 -4
  205. package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
  206. package/src/runtime/cyberia-server/Dockerfile +9 -2
  207. package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
  208. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  209. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  210. package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
  211. package/src/runtime/engine-cyberia/compose.env +17 -5
  212. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  213. package/src/server/build/coverage.js +113 -42
  214. package/src/server/build/package.js +55 -12
  215. package/src/server/build/testing.js +59 -9
  216. package/src/server/network/middlewares.js +8 -2
  217. package/src/server/network/underpost-gateway.js +113 -14
  218. package/src/server/ops/logger.js +70 -35
  219. package/src/server/runtime/conf.js +40 -9
  220. package/src/server/runtime/start.js +11 -2
  221. package/src/server/storage/data-query.js +16 -0
  222. package/src/server/storage/downloader.js +62 -14
  223. package/src/server/storage/zip.js +153 -0
  224. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  225. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  226. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
  227. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  228. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  229. package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
  230. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  231. package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
  232. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  233. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  234. package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
  235. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  236. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  237. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  238. package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
  239. package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
  240. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  241. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  242. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  243. package/test/unit/catalog.test.js +13 -2
  244. package/test/unit/client-build-docs.test.js +97 -70
  245. package/test/unit/client-bundle.test.js +313 -0
  246. package/test/unit/conf-resolution.test.js +58 -7
  247. package/test/unit/coverage-artifact.test.js +120 -29
  248. package/test/unit/cyberia/instance-backup.test.js +183 -0
  249. package/test/unit/cyberia/instance-data.test.js +314 -0
  250. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  251. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  252. package/test/unit/cyberia/stat-balance.test.js +69 -0
  253. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  254. package/test/unit/deploy-log-table.test.js +76 -5
  255. package/test/unit/downloader-integrity.test.js +98 -0
  256. package/test/unit/file-reference-registry.test.js +141 -0
  257. package/test/unit/fs-storage-paths.test.js +596 -0
  258. package/test/unit/gateway-pod-fetch.test.js +95 -0
  259. package/test/unit/letsencrypt-issuer.test.js +101 -0
  260. package/test/unit/logger-redaction.test.js +29 -0
  261. package/test/unit/package.test.js +70 -16
  262. package/test/unit/prepare-host.test.js +84 -1
  263. package/test/unit/release-bump.test.js +3 -1
  264. package/test/unit/start-options.test.js +12 -0
  265. package/test/unit/test-tiers.test.js +27 -0
  266. package/test/unit/zip-archive.test.js +142 -0
  267. package/vitest.config.js +11 -3
  268. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  269. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
@@ -0,0 +1,139 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getFallbackMapCodes } from '../../api/cyberia-instance/cyberia-fallback-world.js';
5
+ import {
6
+ DEFAULT_AUDIO_BANK,
7
+ DEFAULT_AUDIO_SETTINGS,
8
+ DEFAULT_MAP_MUSIC,
9
+ buildAudioEventBindings,
10
+ } from '../../api/cyberia-server-defaults/cyberia-server-defaults.js';
11
+ import { CyberiaAudioService } from '../../api/cyberia-audio/cyberia-audio.service.js';
12
+ import { CyberiaMapAudioConfService } from '../../api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js';
13
+ import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
14
+
15
+ /**
16
+ * Resolves the canonical audio defaults against a list of map codes.
17
+ *
18
+ * The vocabulary, the bank and the bindings are owned by cyberia-server-defaults; this only states
19
+ * them per map. Every map of a world carries the same bed and the same bindings, so a world sounds
20
+ * like itself wherever the player stands, and what changes a place is the event that fires there.
21
+ *
22
+ * @param {string[]} mapCodes - Map codes the world declares.
23
+ * @returns {{assets: string[], settings: object, events: object[], maps: object[]}}
24
+ */
25
+ const audioConfigForMaps = (mapCodes) => ({
26
+ // The codes the world needs imported; the client fetches every one of them by code.
27
+ assets: DEFAULT_AUDIO_BANK.map(({ code }) => code),
28
+ settings: DEFAULT_AUDIO_SETTINGS,
29
+ events: buildAudioEventBindings(),
30
+ maps: mapCodes.map((mapCode) => ({ mapCode, defaultMusic: DEFAULT_MAP_MUSIC })),
31
+ });
32
+
33
+ /** The canonical audio configuration of the fallback world. */
34
+ const fallbackAudioConfig = () => audioConfigForMaps(getFallbackMapCodes());
35
+
36
+ // The renderer lives in cyberia-audio, a sibling repository this one does not contain (see
37
+ // .gitignore, alongside cyberia-server and cyberia-client). Named once so the runtime and the
38
+ // suites that need real bytes ask the same question of the same path.
39
+ const AUDIO_RENDERER_URL = new URL('../../../cyberia-audio/src/dispatcher.js', import.meta.url);
40
+
41
+ /** Whether the cyberia-audio renderer is present beside this repository. */
42
+ const hasAudioRenderer = () => fs.existsSync(AUDIO_RENDERER_URL);
43
+
44
+ /**
45
+ * Loads the cyberia-audio dispatcher, or says plainly what is missing.
46
+ *
47
+ * A bare dynamic import fails here with a resolver message that names neither the project nor
48
+ * what to do about it, and a fresh checkout hits that before anything else.
49
+ *
50
+ * @returns {Promise<{dispatch: Function}>}
51
+ */
52
+ async function loadAudioRenderer() {
53
+ if (!hasAudioRenderer()) {
54
+ throw new Error(
55
+ `cyberia-audio renderer not found at ${fileURLToPath(AUDIO_RENDERER_URL)}. It is a sibling ` +
56
+ 'repository, not part of engine — clone it beside this one to record the audio bank.',
57
+ );
58
+ }
59
+ return await import(AUDIO_RENDERER_URL.href);
60
+ }
61
+
62
+ /**
63
+ * Records the fallback audio the world needs, as `<code>.wav` + `<code>.json` pairs.
64
+ *
65
+ * Recording is where the bytes are produced and nowhere else: the client ships no audio and
66
+ * fetches every asset from engine-cyberia by code, so there is no bundle to build alongside these.
67
+ *
68
+ * @param {{recordsPath: string}} params - Output directory for the recorded pairs.
69
+ * @returns {Promise<object>} The fallback audio configuration these recordings satisfy.
70
+ */
71
+ async function prepareFallbackAudio({ recordsPath }) {
72
+ const { dispatch } = await loadAudioRenderer();
73
+ const config = fallbackAudioConfig();
74
+ await fs.ensureDir(recordsPath);
75
+ for (const { code, bus, options } of DEFAULT_AUDIO_BANK) {
76
+ const output = path.resolve(recordsPath, `${code}.wav`);
77
+ await dispatch(bus, code, { sampleRate: 22050, ...options }, { play: false, record: true, path: output });
78
+ }
79
+ return config;
80
+ }
81
+
82
+ /**
83
+ * Imports the bank and writes one complete audio configuration per map.
84
+ *
85
+ * @param {object} config - As {@link audioConfigForMaps} resolves it.
86
+ * @param {{recordsPath: string}} params - Directory holding the recorded pairs.
87
+ * @param {{host: string, path: string}} options - Provider context.
88
+ * @returns {Promise<{assets: object[], maps: object[]}>}
89
+ */
90
+ async function seedMapAudio(config, { recordsPath }, options) {
91
+ const assets = await CyberiaAudioService.importRecords({ recordsPath, codes: config.assets }, options);
92
+ if (assets.length !== config.assets.length) throw new Error('Seeding audio requires every WAV and manifest pair');
93
+ const maps = [];
94
+ for (const map of config.maps) {
95
+ // The seed states the whole configuration, so re-running it converges: a binding whose logic
96
+ // event this bank no longer declares is dropped rather than left behind pointing at an asset
97
+ // the client can no longer resolve.
98
+ maps.push(
99
+ await CyberiaMapAudioConfService.assign(
100
+ { ...map, settings: config.settings, events: config.events, replaceEvents: true },
101
+ options,
102
+ ),
103
+ );
104
+ }
105
+ return { assets, maps };
106
+ }
107
+
108
+ const seedFallbackAudio = ({ recordsPath }, options) =>
109
+ seedMapAudio(fallbackAudioConfig(), { recordsPath }, options);
110
+
111
+ /**
112
+ * Scores one instance's maps with the canonical bank.
113
+ *
114
+ * Every map the instance declares is scored with the same bank, bed and bindings the fallback
115
+ * world uses, so any world sounds the way that one does.
116
+ *
117
+ * @param {{instanceCode: string, recordsPath: string}} params - Target instance and records directory.
118
+ * @param {{host: string, path: string}} options - Provider context.
119
+ * @returns {Promise<{assets: object[], maps: object[]}>}
120
+ * @throws {Error} When the instance does not exist or declares no maps.
121
+ */
122
+ async function seedInstanceAudio({ instanceCode, recordsPath }, options) {
123
+ const CyberiaInstance = DataBaseProviderService.getModel('CyberiaInstance', options);
124
+ const instance = await CyberiaInstance.findOne({ code: instanceCode }).select('cyberiaMapCodes').lean();
125
+ if (!instance) throw new Error(`cyberia-instance not found for code="${instanceCode}"`);
126
+ const mapCodes = instance.cyberiaMapCodes ?? [];
127
+ if (mapCodes.length === 0) throw new Error(`cyberia-instance "${instanceCode}" declares no maps`);
128
+ return seedMapAudio(audioConfigForMaps(mapCodes), { recordsPath }, options);
129
+ }
130
+
131
+ export {
132
+ audioConfigForMaps,
133
+ fallbackAudioConfig,
134
+ hasAudioRenderer,
135
+ loadAudioRenderer,
136
+ prepareFallbackAudio,
137
+ seedFallbackAudio,
138
+ seedInstanceAudio,
139
+ };
@@ -28,6 +28,7 @@
28
28
  * @namespace SemanticLayerGeneratorSkin
29
29
  */
30
30
  import { readFileSync } from 'fs';
31
+ import { STAT_DEFAULTS } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
31
32
  import path from 'path';
32
33
  import { fileURLToPath } from 'url';
33
34
  import crypto from 'crypto';
@@ -1070,14 +1071,7 @@ function generateSkinMultiFrame(options, _descriptor) {
1070
1071
  description: `Procedurally generated character skin (seed: ${seed})`,
1071
1072
  activable: true,
1072
1073
  },
1073
- stats: {
1074
- effect: 0,
1075
- resistance: 0,
1076
- agility: 0,
1077
- range: 0,
1078
- intelligence: 0,
1079
- utility: 0,
1080
- },
1074
+ stats: { ...STAT_DEFAULTS },
1081
1075
  ledger: { type: 'OFF_CHAIN' },
1082
1076
  seed: localSeedToUUIDv4(`${seed}:${itemId}`),
1083
1077
  },
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import crypto from 'crypto';
20
+ import { generateRandomStats } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
20
21
 
21
22
  import { createRng, seedToInt, createNoise2D, generateShape, listShapes } from './shape-generator.js';
22
23
  import { loggerFactory } from '../../server/ops/logger.js';
@@ -703,14 +704,7 @@ export function generateMultiFrame(options) {
703
704
  description: `Procedurally generated ${descriptor.semanticTags.join(', ')} (seed: ${seed})`,
704
705
  activable: true,
705
706
  },
706
- stats: {
707
- effect: hashMod(seed + ':effect', 11),
708
- resistance: hashMod(seed + ':resistance', 11),
709
- agility: hashMod(seed + ':agility', 11),
710
- range: hashMod(seed + ':range', 11),
711
- intelligence: hashMod(seed + ':intelligence', 11),
712
- utility: hashMod(seed + ':utility', 11),
713
- },
707
+ stats: generateRandomStats(undefined, undefined, createRng(seedToInt(seed + ':stats'))),
714
708
  ledger: { type: 'OFF_CHAIN' },
715
709
  seed: seedToUUIDv4(seed + ':' + itemId),
716
710
  },
@@ -726,16 +720,6 @@ export function generateMultiFrame(options) {
726
720
  };
727
721
  }
728
722
 
729
- /**
730
- * Deterministic modulo hash for stat generation.
731
- * @param {string} str
732
- * @param {number} mod
733
- * @returns {number}
734
- */
735
- function hashMod(str, mod) {
736
- return ((hashString(str) % mod) + mod) % mod;
737
- }
738
-
739
723
  /* ═══════════════════════════════════════════════════════════════════════════
740
724
  * EXPORTS
741
725
  * ═══════════════════════════════════════════════════════════════════════════ */
@@ -0,0 +1,81 @@
1
+ import {
2
+ STAT_MODIFIER_MAX,
3
+ STAT_MODIFIER_MIN,
4
+ STAT_TYPES,
5
+ statBoundsForType,
6
+ validateStatModifier,
7
+ validateStats,
8
+ } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
9
+
10
+ /**
11
+ * Stat balancing for every object layer the content pipeline writes.
12
+ *
13
+ * One policy resolves the bounds each stat may take, then either clamps the stats the content
14
+ * carries or draws new ones inside those bounds. The same policy answers every write path, so a
15
+ * run with a given policy over given content is deterministic.
16
+ *
17
+ * @typedef {Object} StatPolicy
18
+ * @property {boolean} [normalize=false] - Bound each stat by the item type's semantic range.
19
+ * @property {boolean} [random=false] - Replace every stat with a value drawn inside its bounds.
20
+ * @property {number} [min] - Narrow every bound from below.
21
+ * @property {number} [max] - Narrow every bound from above.
22
+ */
23
+
24
+ /** Whether a policy changes anything at all. */
25
+ export const statPolicyActive = ({ normalize = false, random = false } = {}) => normalize || random;
26
+
27
+ /**
28
+ * The inclusive `[min, max]` each stat may take under a policy for one item type.
29
+ *
30
+ * The semantic range wins: `min` and `max` narrow it, and a request that leaves no overlap pins
31
+ * the stat to the semantic edge nearest the request. Without `normalize` the contract range is
32
+ * the whole range.
33
+ *
34
+ * @param {string} itemType
35
+ * @param {StatPolicy} policy
36
+ * @returns {Record<string,[number,number]>}
37
+ */
38
+ export function resolveStatBounds(itemType, { normalize = false, min, max } = {}) {
39
+ if (min !== undefined) validateStatModifier(min);
40
+ if (max !== undefined) validateStatModifier(max);
41
+ if (min !== undefined && max !== undefined && min > max)
42
+ throw new RangeError('Stat minimum must not exceed maximum.');
43
+ const semantic = normalize
44
+ ? statBoundsForType(itemType)
45
+ : Object.fromEntries(STAT_TYPES.map((key) => [key, [STAT_MODIFIER_MIN, STAT_MODIFIER_MAX]]));
46
+ return Object.fromEntries(
47
+ STAT_TYPES.map((key) => {
48
+ const [lo, hi] = semantic[key];
49
+ const low = Math.max(lo, min ?? lo);
50
+ const high = Math.min(hi, max ?? hi);
51
+ if (low <= high) return [key, [low, high]];
52
+ // No overlap: the semantic edge the request points at.
53
+ const pinned = (min ?? lo) > hi ? hi : lo;
54
+ return [key, [pinned, pinned]];
55
+ }),
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Applies a policy to one item's stats.
61
+ *
62
+ * @param {Object} params
63
+ * @param {Object} params.stats - Stats as the content carries them.
64
+ * @param {string} params.itemType
65
+ * @param {StatPolicy} [params.policy]
66
+ * @param {() => number} [params.random=Math.random] - Source in [0, 1) for a random draw.
67
+ * @returns {Record<string,number>} The balanced stats, in canonical order.
68
+ */
69
+ export function balanceStats({ stats, itemType, policy = {}, random = Math.random }) {
70
+ const current = validateStats(stats);
71
+ const bounds = resolveStatBounds(itemType, policy);
72
+ return Object.fromEntries(
73
+ STAT_TYPES.map((key) => {
74
+ const [lo, hi] = bounds[key];
75
+ if (!policy.random) return [key, Math.min(hi, Math.max(lo, current[key]))];
76
+ const value = random();
77
+ if (!Number.isFinite(value) || value < 0 || value >= 1) throw new RangeError('Random source must return [0, 1).');
78
+ return [key, Math.floor(value * (hi - lo + 1)) + lo];
79
+ }),
80
+ );
81
+ }
@@ -0,0 +1,23 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { generateRandomStats, validateStats } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
3
+ import { PROGRESSION_RULES_DEFAULTS } from '../../api/cyberia-server-defaults/cyberia-server-defaults.js';
4
+ import { generateStatContract } from './stat-contract-generator.js';
5
+
6
+ export function registerStatCommands(program) {
7
+ const stats = program.command('stats').description('Validate signed stats and inspect progression rules.');
8
+ stats.command('random').option('--min <number>').option('--max <number>').action((options) => {
9
+ const min = options.min === undefined ? undefined : Number(options.min);
10
+ const max = options.max === undefined ? undefined : Number(options.max);
11
+ console.log(JSON.stringify(generateRandomStats(min, max), null, 2));
12
+ });
13
+ stats.command('validate <file>').action(async (file) => {
14
+ const input = JSON.parse(await readFile(file, 'utf8'));
15
+ const records = Array.isArray(input) ? input : [input];
16
+ for (const record of records) validateStats(record.data?.stats ?? record.stats ?? record);
17
+ console.log(JSON.stringify({ valid: true, records: records.length }));
18
+ });
19
+ stats.command('progression').action(() => console.log(JSON.stringify(PROGRESSION_RULES_DEFAULTS, null, 2)));
20
+ program.command('stat-contract').option('--check', 'Check generated Go and C files.').action(async (options) => {
21
+ console.log(JSON.stringify(await generateStatContract({ check: !!options.check })));
22
+ });
23
+ }
@@ -0,0 +1,101 @@
1
+ import { buildBootContractArtifacts } from './boot-contract-fixtures.js';
2
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { resolve, dirname } from 'node:path';
9
+ import { STAT_TYPES, STAT_DEFINITIONS, STAT_MODIFIER_MIN, STAT_MODIFIER_MAX, STAT_EFFECTIVE_FLOORS, STAT_SCALES, STAT_REGEN_PER_RESISTANCE } from '../../client/components/cyberia/SharedDefaultsCyberia.js';
10
+ import { PROGRESSION_RULES_DEFAULTS, PROGRESSION_RULE_LIMITS } from '../../api/cyberia-server-defaults/cyberia-server-defaults.js';
11
+
12
+ const field = (key) => key[0].toUpperCase() + key.slice(1);
13
+ const values = (stats) => STAT_TYPES.map((key) => stats[key]).join(', ');
14
+
15
+ export function buildStatContractArtifacts() {
16
+ const go = `// Code generated by Cyberia stat-contract. DO NOT EDIT.
17
+ package game
18
+ const StatCount = ${STAT_TYPES.length}
19
+ const StatModifierMin = ${STAT_MODIFIER_MIN}
20
+ const StatModifierMax = ${STAT_MODIFIER_MAX}
21
+ func (r ProgressionRules) validScalarBounds() bool { return ${Object.entries(PROGRESSION_RULE_LIMITS).map(([key, [min, max]]) => `r.${field(key)} >= ${min} && r.${field(key)} <= ${max}`).join(' && ')} }
22
+ var statNames = [StatCount]string{${STAT_TYPES.map(JSON.stringify).join(', ')}}
23
+ var statEffectiveFloors = [StatCount]float64{${values(STAT_EFFECTIVE_FLOORS)}}
24
+ // statScales is what one point of each stat is worth in the simulation.
25
+ var statScales = ComputedStats{${STAT_TYPES.map((key) => `${field(key)}: ${STAT_SCALES[key]}`).join(', ')}}
26
+ const statRegenPerResistance = ${STAT_REGEN_PER_RESISTANCE}
27
+ type Stats struct {
28
+ ${STAT_TYPES.map((key) => `${field(key)} int \`json:"${key}"\``).join('\n')}
29
+ }
30
+ type ComputedStats struct {
31
+ ${STAT_TYPES.map((key) => `${field(key)} float64 \`json:"${key}"\``).join('\n')}
32
+ }
33
+ func (s Stats) computed() ComputedStats { return ComputedStats{${STAT_TYPES.map((key) => `${field(key)}: float64(s.${field(key)})`).join(', ')}} }
34
+ func (s ComputedStats) Values() [StatCount]float64 { return [StatCount]float64{${STAT_TYPES.map((key) => `s.${field(key)}`).join(', ')}} }
35
+ func statsFromValues(v [StatCount]float64) ComputedStats { return ComputedStats{${STAT_TYPES.map((key, i) => `${field(key)}: v[${i}]`).join(', ')}} }
36
+ func defaultProgressionRules() ProgressionRules { return ProgressionRules{
37
+ ${Object.entries(PROGRESSION_RULES_DEFAULTS).map(([key, value]) => `${field(key)}: ${typeof value === 'object' ? `ComputedStats{${values(value)}}` : value},`).join('\n')}
38
+ } }
39
+ `;
40
+ const header = `/* Code generated by Cyberia stat-contract. DO NOT EDIT. */
41
+ #ifndef CYBERIA_STAT_CONTRACT_H
42
+ #define CYBERIA_STAT_CONTRACT_H
43
+ #define CYBERIA_STAT_COUNT ${STAT_TYPES.length}
44
+ static const char* const CYBERIA_STAT_NAMES[CYBERIA_STAT_COUNT] = {${STAT_TYPES.map(JSON.stringify).join(', ')}};
45
+ typedef struct {
46
+ ${STAT_TYPES.map((key) => ' int ' + key + ';').join('\n')}
47
+ } Stats;
48
+ static inline void cyberia_stats_values(const Stats* stats, int values[CYBERIA_STAT_COUNT]) {
49
+ ${STAT_TYPES.map((key, i) => ' values[' + i + '] = stats->' + key + ';').join('\n')}
50
+ }
51
+ static inline Stats cyberia_stats_from_values(const int values[CYBERIA_STAT_COUNT]) {
52
+ return (Stats){${STAT_TYPES.map((key, i) => 'values[' + i + ']').join(', ')}};
53
+ }
54
+ static const char* const CYBERIA_STAT_ICONS[CYBERIA_STAT_COUNT] = {${STAT_TYPES.map((key) => JSON.stringify('stat-' + key)).join(', ')}};
55
+ static const char* const CYBERIA_STAT_LABELS[CYBERIA_STAT_COUNT] = {${STAT_DEFINITIONS.map(({ title }) => JSON.stringify(title)).join(', ')}};
56
+ #endif
57
+ `;
58
+ const temporary = mkdtempSync(join(tmpdir(), 'cyberia-stat-contract-'));
59
+ let formatted;
60
+ try {
61
+ const source = join(temporary, 'contract.go');
62
+ writeFileSync(source, go);
63
+ execFileSync('gofmt', ['-w', source], { stdio: 'inherit', timeout: 30000 });
64
+ formatted = readFileSync(source, 'utf8');
65
+ } finally {
66
+ rmSync(temporary, { recursive: true, force: true });
67
+ }
68
+ return {
69
+ 'cyberia-server/game/stat_contract_generated.go': formatted,
70
+ 'cyberia-client/src/domain/stat_contract_generated.h': header,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Whether cyberia-server and cyberia-client sit beside this repository.
76
+ *
77
+ * Both are gitignored siblings (see .gitignore, alongside cyberia-audio), so a checkout that
78
+ * publishes only this repo's tracked tree — the engine-cyberia npm mirrors, for one — never has
79
+ * them. {@link generateStatContract} writes into both; callers that only run in a full
80
+ * multi-repo checkout should skip rather than fail on a missing sibling.
81
+ *
82
+ * @param {string} [root] - Defaults to this repository's own root.
83
+ * @returns {boolean}
84
+ */
85
+ export function hasCyberiaSiblings(root = fileURLToPath(new URL('../../../', import.meta.url))) {
86
+ return existsSync(resolve(root, 'cyberia-server')) && existsSync(resolve(root, 'cyberia-client'));
87
+ }
88
+
89
+ export async function generateStatContract({ root = fileURLToPath(new URL('../../../', import.meta.url)), check = false } = {}) {
90
+ const artifacts = { ...buildStatContractArtifacts(), ...buildBootContractArtifacts() };
91
+ for (const [path, content] of Object.entries(artifacts)) {
92
+ const target = resolve(root, path);
93
+ if (check) {
94
+ if (await readFile(target, 'utf8') !== content) throw new Error(`Regenerate ${path} with cyberia stat-contract.`);
95
+ } else {
96
+ await mkdir(dirname(target), { recursive: true });
97
+ await writeFile(target, content);
98
+ }
99
+ }
100
+ return Object.keys(artifacts);
101
+ }
@@ -38,17 +38,21 @@ RUN set -eux; \
38
38
  underpost --version
39
39
 
40
40
  # Path + server layout is the contract with conf.instances.json mmo-client cmd,
41
- # which execs `python3 /home/dd/engine/cyberia-client/docker-driver.py <port>
42
- # /home/dd/engine/cyberia-client/bin` in both development and production.
41
+ # which execs `python3 /home/dd/engine/cyberia-client/wasm-driver.py --port=<port>
42
+ # --directory=/home/dd/engine/cyberia-client/bin --data-server-url=<url>` in both
43
+ # development and production.
43
44
  # Keep this identical to Dockerfile.dev so prod and dev images are interchangeable.
44
45
  WORKDIR /home/dd/engine/cyberia-client
45
46
 
46
- COPY --from=builder /cyberia-client/docker-driver.py ./docker-driver.py
47
+ COPY --from=builder /cyberia-client/wasm-driver.py ./wasm-driver.py
47
48
  COPY --from=builder /cyberia-client/bin ./bin/
48
49
 
49
50
  ENV PYTHONUNBUFFERED=1
50
51
  ENV CYBERIA_PORT=8081
52
+ # Mirrors the --data-server-url default in wasm-driver.py: the CMD below always passes the flag,
53
+ # so leaving this unset would hand the client an empty origin rather than that default.
54
+ ENV CYBERIA_DATA_SERVER_URL=https://cyberiaonline.com
51
55
 
52
56
  EXPOSE 8081
53
57
 
54
- CMD ["sh", "-c", "exec python3 docker-driver.py ${CYBERIA_PORT} bin"]
58
+ CMD ["sh", "-c", "exec python3 wasm-driver.py --port=${CYBERIA_PORT} --directory=bin --data-server-url=${CYBERIA_DATA_SERVER_URL}"]
@@ -71,11 +71,14 @@ RUN set -eux; \
71
71
 
72
72
  WORKDIR /home/dd/engine/cyberia-client
73
73
 
74
- COPY --from=builder /cyberia-client/docker-driver.py ./docker-driver.py
74
+ COPY --from=builder /cyberia-client/wasm-driver.py ./wasm-driver.py
75
75
  COPY --from=builder /cyberia-client/bin ./bin/
76
76
 
77
77
  ENV CYBERIA_PORT=8082
78
+ # Mirrors the --data-server-url default in wasm-driver.py: the CMD below always passes the flag,
79
+ # so leaving this unset would hand the client an empty origin rather than that default.
80
+ ENV CYBERIA_DATA_SERVER_URL=https://cyberiaonline.com
78
81
 
79
82
  EXPOSE 8081 8082
80
83
 
81
- CMD ["sh", "-c", "exec python3 docker-driver.py ${CYBERIA_PORT} bin"]
84
+ CMD ["sh", "-c", "exec python3 wasm-driver.py --port=${CYBERIA_PORT} --directory=bin --data-server-url=${CYBERIA_DATA_SERVER_URL}"]
@@ -1,5 +1,5 @@
1
1
  # --- Build Image ---
2
- FROM golang:1.24 AS builder
2
+ FROM golang:1.25 AS builder
3
3
 
4
4
  WORKDIR /build
5
5
 
@@ -45,4 +45,11 @@ COPY public/ ./public/
45
45
 
46
46
  EXPOSE 8081
47
47
 
48
- ENTRYPOINT ["/home/dd/engine/cyberia-server/server"]
48
+ # Both Data Server endpoints are flags, not environment reads (cmd/cyberia-server/main.go), and
49
+ # the server refuses to start without them. Passing them here from the environment is what lets a
50
+ # plain `docker run` or a Compose service that sets no command still start: a Kubernetes pod spec
51
+ # supplies its own command and overrides this.
52
+ ENV CYBERIA_DATA_SERVER_URL=http://engine-cyberia
53
+ ENV CYBERIA_DATA_SERVER_GRPC=engine-cyberia-runtime:50051
54
+
55
+ CMD ["sh", "-c", "exec /home/dd/engine/cyberia-server/server --data-server-url=${CYBERIA_DATA_SERVER_URL} --data-server-grpc=${CYBERIA_DATA_SERVER_GRPC}"]
@@ -59,4 +59,11 @@ COPY public/ ./public/
59
59
 
60
60
  EXPOSE 8081
61
61
 
62
- ENTRYPOINT ["/home/dd/engine/cyberia-server/server"]
62
+ # Both Data Server endpoints are flags, not environment reads (cmd/cyberia-server/main.go), and
63
+ # the server refuses to start without them. Passing them here from the environment is what lets a
64
+ # plain `docker run` or a Compose service that sets no command still start: a Kubernetes pod spec
65
+ # supplies its own command and overrides this.
66
+ ENV CYBERIA_DATA_SERVER_URL=http://engine-cyberia
67
+ ENV CYBERIA_DATA_SERVER_GRPC=engine-cyberia-runtime:50051
68
+
69
+ CMD ["sh", "-c", "exec /home/dd/engine/cyberia-server/server --data-server-url=${CYBERIA_DATA_SERVER_URL} --data-server-grpc=${CYBERIA_DATA_SERVER_GRPC}"]
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.73
9
+ ARG UNDERPOST_VERSION=3.3.77
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.73
9
+ ARG UNDERPOST_VERSION=3.3.77
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -6,7 +6,7 @@
6
6
  # Stage 1 — builder: clone the private deploy, build it, then scrub secrets.
7
7
  # ---------------------------------------------------------------------------
8
8
  FROM rockylinux/rockylinux:9 AS builder
9
- ARG UNDERPOST_VERSION=3.3.73
9
+ ARG UNDERPOST_VERSION=3.3.77
10
10
  # Pin Node to an exact patch: dnf's nodejs:24 module lags (24.14.1) while
11
11
  # underpost's dependencies require >=24.15.0, so install the official binary.
12
12
  ARG NODE_VERSION=24.15.0
@@ -72,7 +72,7 @@ RUN --mount=type=secret,id=github_token \
72
72
  # --- install deps + env, then replay build-safe itc provisioning ----------
73
73
  npm install; \
74
74
  node bin app load --env development --args deploy-id=dd-cyberia; \
75
- node bin fs --pull --recursive --deploy-id dd-cyberia --storage-file-path './engine-private/conf/dd-cyberia/storage.engine-cyberia.json'; \
75
+ node bin fs --pull --tracked --deploy-id dd-cyberia --storage-id engine-cyberia; \
76
76
  ( cd /home/dd/engine/hardhat && npm install --include=dev ); \
77
77
  ( cd /home/dd/engine && underpost clone underpostnet/cyberia-server ); \
78
78
  \
@@ -43,25 +43,37 @@ VALKEY_PORT=6379
43
43
  # cyberia-server-* -> stable engine gateway for HTTP and the raw engine runtime
44
44
  # for gRPC. The public engine-cyberia name belongs to nginx, avoiding ambiguous
45
45
  # Docker DNS while keeping one stable HTTP endpoint for every module.
46
- ENGINE_API_BASE_URL=http://engine-cyberia
47
- ENGINE_GRPC_ADDRESS=engine-cyberia-runtime:50051
46
+ CYBERIA_DATA_SERVER_URL=http://engine-cyberia
47
+ CYBERIA_DATA_SERVER_GRPC=engine-cyberia-runtime:50051
48
48
 
49
49
  # Client-visible Content Authority origin, forwarded to browser/Raylib clients
50
50
  # for every engine-cyberia content/asset/metadata request. Host-reachable
51
51
  # (Docker publishes the raw engine runtime on host :4005). Unlike the internal
52
- # ENGINE_API_BASE_URL, this value is consumed outside the Compose network:
52
+ # CYBERIA_DATA_SERVER_URL, this value is consumed outside the Compose network:
53
53
  # the two responsibilities must never overwrite each other.
54
54
  ENGINE_PUBLIC_URL=http://localhost:4005
55
55
  ENGINE_GRPC_RELOAD_INTERVAL_SEC=300
56
56
 
57
- # --- Browser-facing client runtime config (injected by docker-driver.py) -----
57
+ # Shared secret between the engine and every game server. It guards the server
58
+ # registry report (server -> engine) and the hot-reload trigger (engine ->
59
+ # server). Unset disables both: a server then stays out of the public list.
60
+ CYBERIA_SERVER_API_KEY=change_me_dev_server_api_key
61
+
62
+ # Public origin of the server gateway. Each server reports this origin plus its
63
+ # own sub-path, which is the URL a client dials.
64
+ CYBERIA_SERVER_PUBLIC_ORIGIN=http://localhost:8081
65
+
66
+ # --- Browser-facing client runtime config (injected by wasm-driver.py) -----
58
67
  # The single WASM bundle resolves its instance from the URL sub-path and builds
59
68
  # its endpoints from these origins. HTTP client/API traffic uses nginx on :80;
60
69
  # WebSocket traffic uses the dedicated multi-path server gateway on :8081.
61
70
  # http://localhost/ -> default, ws://localhost:8081/ws
62
71
  # http://localhost/FOREST -> FOREST, ws://localhost:8081/FOREST/ws
63
72
  CYBERIA_WS_ORIGIN=ws://localhost:8081
64
- CYBERIA_ENGINE_API_ORIGIN=http://localhost
73
+ # The browser's own route to the Data Server, through nginx on :80. Distinct from the internal
74
+ # CYBERIA_DATA_SERVER_URL above: both name a Data Server origin, but from opposite sides of the
75
+ # Compose network, and one file cannot hold two values under one name.
76
+ CYBERIA_CLIENT_DATA_SERVER_URL=http://localhost
65
77
  CYBERIA_DEFAULT_INSTANCE=amethyst-strata-expansion
66
78
 
67
79
  # cyberia-server logging. This is the dev stack, so default to DEBUG. Set