cyberia 3.2.9 → 3.2.22

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 (184) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +7 -0
  2. package/.github/workflows/engine-cyberia.ci.yml +14 -2
  3. package/.github/workflows/ghpkg.ci.yml +1 -0
  4. package/.github/workflows/npmpkg.ci.yml +10 -5
  5. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  6. package/.github/workflows/release.cd.yml +1 -0
  7. package/.vscode/extensions.json +9 -9
  8. package/.vscode/settings.json +20 -4
  9. package/CHANGELOG.md +363 -1
  10. package/CLI-HELP.md +975 -1061
  11. package/README.md +190 -348
  12. package/bin/build.js +102 -125
  13. package/bin/build.template.js +33 -0
  14. package/bin/cyberia.js +238 -56
  15. package/bin/deploy.js +16 -3
  16. package/bin/index.js +238 -56
  17. package/bump.config.js +26 -0
  18. package/conf.js +131 -24
  19. package/deployment.yaml +76 -2
  20. package/hardhat/package-lock.json +113 -144
  21. package/hardhat/package.json +4 -3
  22. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  23. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  24. package/manifests/deployment/dd-cyberia-development/deployment.yaml +76 -2
  25. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  26. package/manifests/kind-config-dev.yaml +8 -0
  27. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  28. package/manifests/mongodb/pv-pvc.yaml +44 -8
  29. package/manifests/mongodb/statefulset.yaml +55 -68
  30. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  31. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  32. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  33. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  34. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  35. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  36. package/manifests/valkey/statefulset.yaml +1 -1
  37. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  38. package/package.json +31 -19
  39. package/scripts/ipxe-setup.sh +52 -49
  40. package/scripts/k3s-node-setup.sh +81 -46
  41. package/scripts/link-local-underpost-cli.sh +6 -0
  42. package/scripts/lxd-vm-setup.sh +193 -8
  43. package/scripts/maas-nat-firewalld.sh +145 -0
  44. package/scripts/test-monitor.sh +250 -0
  45. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  46. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  47. package/src/api/core/core.router.js +19 -14
  48. package/src/api/core/core.service.js +5 -5
  49. package/src/api/crypto/crypto.router.js +18 -12
  50. package/src/api/crypto/crypto.service.js +3 -3
  51. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  52. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  53. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  55. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  56. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  57. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  58. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  59. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  60. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  61. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  62. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  63. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  64. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  65. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  66. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  67. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  68. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  69. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  70. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  71. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  72. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  73. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  74. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  75. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  76. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +458 -0
  77. package/src/api/default/default.router.js +22 -18
  78. package/src/api/default/default.service.js +5 -5
  79. package/src/api/document/document.router.js +28 -23
  80. package/src/api/document/document.service.js +100 -23
  81. package/src/api/file/file.router.js +19 -13
  82. package/src/api/file/file.service.js +9 -7
  83. package/src/api/instance/instance.router.js +29 -24
  84. package/src/api/instance/instance.service.js +6 -6
  85. package/src/api/ipfs/ipfs.router.js +21 -16
  86. package/src/api/ipfs/ipfs.service.js +8 -8
  87. package/src/api/object-layer/object-layer.router.js +512 -507
  88. package/src/api/object-layer/object-layer.service.js +17 -14
  89. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  90. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  91. package/src/api/test/test.router.js +17 -12
  92. package/src/api/types.js +24 -0
  93. package/src/api/user/guest.service.js +5 -4
  94. package/src/api/user/user.router.js +297 -288
  95. package/src/api/user/user.service.js +100 -35
  96. package/src/cli/baremetal.js +132 -101
  97. package/src/cli/cluster.js +700 -232
  98. package/src/cli/db.js +59 -60
  99. package/src/cli/deploy.js +291 -294
  100. package/src/cli/env.js +1 -4
  101. package/src/cli/fs.js +13 -3
  102. package/src/cli/image.js +58 -4
  103. package/src/cli/index.js +127 -15
  104. package/src/cli/ipfs.js +4 -6
  105. package/src/cli/kubectl.js +4 -1
  106. package/src/cli/lxd.js +1099 -223
  107. package/src/cli/monitor.js +396 -9
  108. package/src/cli/release.js +355 -146
  109. package/src/cli/repository.js +169 -30
  110. package/src/cli/run.js +347 -117
  111. package/src/cli/secrets.js +11 -2
  112. package/src/cli/test.js +9 -3
  113. package/src/client/Default.index.js +9 -3
  114. package/src/client/components/core/Auth.js +5 -0
  115. package/src/client/components/core/ClientEvents.js +76 -0
  116. package/src/client/components/core/EventBus.js +4 -0
  117. package/src/client/components/core/Modal.js +82 -41
  118. package/src/client/components/core/PanelForm.js +14 -10
  119. package/src/client/components/core/Worker.js +162 -363
  120. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  121. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  122. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +55 -1
  123. package/src/client/public/cyberia-docs/ARCHITECTURE.md +223 -361
  124. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  125. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  126. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +212 -185
  127. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  128. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  129. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +23 -1
  130. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  131. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  132. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  133. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  134. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  135. package/src/client/sw/core.sw.js +174 -112
  136. package/src/db/DataBaseProvider.js +115 -15
  137. package/src/db/mariadb/MariaDB.js +2 -1
  138. package/src/db/mongo/MongoBootstrap.js +657 -0
  139. package/src/db/mongo/MongooseDB.js +130 -21
  140. package/src/grpc/cyberia/grpc-server.js +25 -57
  141. package/src/index.js +1 -1
  142. package/src/runtime/cyberia-client/Dockerfile +10 -7
  143. package/src/runtime/cyberia-client/Dockerfile.dev +67 -0
  144. package/src/runtime/cyberia-server/Dockerfile +11 -6
  145. package/src/runtime/cyberia-server/Dockerfile.dev +47 -0
  146. package/src/runtime/express/Express.js +2 -2
  147. package/src/runtime/wp/Dockerfile +3 -3
  148. package/src/runtime/wp/Wp.js +8 -5
  149. package/src/server/auth.js +2 -2
  150. package/src/server/catalog-underpost.js +61 -0
  151. package/src/server/catalog.js +77 -0
  152. package/src/server/client-build-docs.js +1 -1
  153. package/src/server/client-build.js +94 -129
  154. package/src/server/conf.js +496 -135
  155. package/src/server/ipfs-client.js +5 -3
  156. package/src/server/process.js +180 -19
  157. package/src/server/proxy.js +9 -2
  158. package/src/server/runtime-status.js +235 -0
  159. package/src/server/runtime.js +1 -1
  160. package/src/server/start.js +44 -11
  161. package/src/server/valkey.js +2 -0
  162. package/src/ws/IoInterface.js +16 -16
  163. package/src/ws/core/channels/core.ws.chat.js +11 -11
  164. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  165. package/src/ws/core/channels/core.ws.stream.js +19 -19
  166. package/src/ws/core/core.ws.connection.js +8 -8
  167. package/src/ws/core/core.ws.server.js +6 -5
  168. package/src/ws/default/channels/default.ws.main.js +10 -10
  169. package/src/ws/default/default.ws.connection.js +4 -4
  170. package/src/ws/default/default.ws.server.js +4 -3
  171. package/test/deploy-monitor.test.js +251 -0
  172. package/bin/file.js +0 -202
  173. package/bin/vs.js +0 -74
  174. package/bin/zed.js +0 -84
  175. package/manifests/deployment/dd-test-development/deployment.yaml +0 -254
  176. package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
  177. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  178. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  179. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  180. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  181. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  182. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  183. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  184. /package/src/client/ssr/{pages → views}/Test.js +0 -0
@@ -13,7 +13,7 @@ import { DefaultManagement } from '../../services/default/default.management.js'
13
13
  import { getApiBaseUrl } from '../../services/core/core.service.js';
14
14
  import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
15
15
  import { getProxyPath } from '../core/Router.js';
16
- import { ENTITY_TYPES, getDefaultCyberiaItemById } from '../cyberia-portal/CommonCyberiaPortal.js';
16
+ import { ENTITY_TYPES, getDefaultCyberiaItemById } from './SharedDefaultsCyberia.js';
17
17
  import '../core/ColorPaletteElement.js';
18
18
 
19
19
  const DEFAULT_ENTITY_TYPE = ENTITY_TYPES.floor;
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Shared Cyberia defaults — isomorphic, dependency-free, importable from
3
+ * **both** Node and the browser.
4
+ *
5
+ * Pure plain-data ESM module: no Node built-ins, no DOM APIs, no
6
+ * environment guards. Every export is a frozen literal, a constant
7
+ * lookup table, or a pure function over those tables. Side-effect free
8
+ * on import.
9
+ *
10
+ * Two responsibilities, no others:
11
+ *
12
+ * 1. **Presentation values** — palette, entity colour keys, status-icon
13
+ * visuals (icon stem + border colour + bounce), camera tunings,
14
+ * render flags. Mirrored 1-to-1 by the C/WASM client's compile-time
15
+ * defaults in `cyberia-client/src/domain/presentation_defaults.h`.
16
+ * Optionally overridable per deployment through the REST endpoint
17
+ * `/api/cyberia-client-hints/:code`.
18
+ *
19
+ * 2. **Shared content vocabulary** — `ITEM_TYPES`, `ENTITY_TYPES`,
20
+ * `DefaultCyberiaItems` registry + lookups, type-to-item mapping,
21
+ * quest step / action type enums. The data shape both the
22
+ * browser-side editor UI and the engine REST controllers need to
23
+ * understand; it is **not** simulation state.
24
+ *
25
+ * STRICT BOUNDARIES
26
+ * -----------------
27
+ * - The cyberia-server (Go) MUST NOT load this file. None of these
28
+ * values influence the authoritative simulation. The server owns the
29
+ * numeric Entity Status Indicator IDs only (see `cyberia-server-defaults`).
30
+ *
31
+ * - The C/WASM cyberia-client embeds *presentation* defaults at compile
32
+ * time and fetches optional overrides through REST. It does NOT carry
33
+ * the JS vocabulary constants — entity-type and item-id strings
34
+ * arrive on the wire and are matched directly.
35
+ *
36
+ * - The browser editor bundles this file via esbuild. It MUST NOT
37
+ * import `cyberia-server-defaults.js` — that would pull simulation
38
+ * rules, economy, and seed content into the browser bundle. All
39
+ * shared vocabulary the editor needs lives here.
40
+ *
41
+ * Naming: this file is intentionally placed under `src/client/` so its
42
+ * URL is `/components/cyberia/SharedDefaultsCyberia.js` when the engine
43
+ * static server resolves it. Node-side importers reach in with a
44
+ * relative path; both halves see the same module instance.
45
+ *
46
+ * @module src/client/components/cyberia/SharedDefaultsCyberia.js
47
+ */
48
+
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+ // Shared content vocabulary
51
+ // ─────────────────────────────────────────────────────────────────────────────
52
+
53
+ /**
54
+ * Canonical set of ObjectLayer item type names. Used as the
55
+ * `data.item.type` discriminator and as the asset directory name on disk.
56
+ *
57
+ * Values intentionally equal their keys — this is a string enum, not a
58
+ * z-order map. Render layer ordering lives in `entity_render.c`.
59
+ *
60
+ * @type {Readonly<Record<string,string>>}
61
+ */
62
+ export const ITEM_TYPES = Object.freeze({
63
+ skin: 'skin',
64
+ breastplate: 'breastplate',
65
+ weapon: 'weapon',
66
+ skill: 'skill',
67
+ coin: 'coin',
68
+ floor: 'floor',
69
+ obstacle: 'obstacle',
70
+ portal: 'portal',
71
+ foreground: 'foreground',
72
+ resource: 'resource',
73
+ });
74
+
75
+ /**
76
+ * Canonical set of entity category names used by the Go simulation,
77
+ * the C/WASM client, and the browser editor UI.
78
+ *
79
+ * @type {Readonly<Record<string,string>>}
80
+ */
81
+ export const ENTITY_TYPES = Object.freeze({
82
+ player: 'player',
83
+ other_player: 'other_player',
84
+ bot: 'bot',
85
+ skill: 'skill',
86
+ coin: 'coin',
87
+ floor: 'floor',
88
+ obstacle: 'obstacle',
89
+ portal: 'portal',
90
+ foreground: 'foreground',
91
+ resource: 'resource',
92
+ });
93
+
94
+ /** Per-entity-type allowlist of item types that may appear on the entity. */
95
+ export const ENTITY_TYPE_TO_ITEM_TYPES = Object.freeze({
96
+ [ENTITY_TYPES.player]: Object.freeze([ITEM_TYPES.skin, ITEM_TYPES.breastplate, ITEM_TYPES.weapon]),
97
+ [ENTITY_TYPES.other_player]: Object.freeze([ITEM_TYPES.skin, ITEM_TYPES.breastplate, ITEM_TYPES.weapon]),
98
+ [ENTITY_TYPES.bot]: Object.freeze([ITEM_TYPES.skin, ITEM_TYPES.weapon]),
99
+ [ENTITY_TYPES.skill]: Object.freeze([ITEM_TYPES.skill]),
100
+ [ENTITY_TYPES.coin]: Object.freeze([ITEM_TYPES.coin]),
101
+ [ENTITY_TYPES.floor]: Object.freeze([ITEM_TYPES.floor]),
102
+ [ENTITY_TYPES.obstacle]: Object.freeze([ITEM_TYPES.obstacle]),
103
+ [ENTITY_TYPES.portal]: Object.freeze([ITEM_TYPES.portal]),
104
+ [ENTITY_TYPES.foreground]: Object.freeze([ITEM_TYPES.foreground]),
105
+ [ENTITY_TYPES.resource]: Object.freeze([ITEM_TYPES.resource]),
106
+ });
107
+
108
+ /** Quest step objective types accepted by the quest-progress engine. */
109
+ export const QUEST_STEPS_TYPES = Object.freeze(['collect', 'talk', 'kill']);
110
+
111
+ /** Action categories accepted by the cyberia-action engine. */
112
+ export const CYBERIA_ACTION_TYPES = Object.freeze(['craft', 'shop', 'storage', 'talk', 'quest-talk']);
113
+
114
+ /**
115
+ * Canonical (itemId → itemType) registry shipped with the engine. Used
116
+ * by the import-default-items seed, the on-chain ObjectLayerToken bridge,
117
+ * the fallback world generator, the CLI tooling, and the browser editor.
118
+ *
119
+ * Adding a new item here is the **only** place it needs to be declared.
120
+ */
121
+ export const DefaultCyberiaItems = [
122
+ { item: { id: 'coin', type: ITEM_TYPES.coin } },
123
+ { item: { id: 'hatchet-skill', type: ITEM_TYPES.skill } },
124
+ { item: { id: 'atlas_pistol_mk2', type: ITEM_TYPES.weapon } },
125
+ { item: { id: 'atlas_pistol_mk2_bullet', type: ITEM_TYPES.skill } },
126
+ { item: { id: 'tim-knife', type: ITEM_TYPES.weapon } },
127
+ { item: { id: 'hatchet', type: ITEM_TYPES.weapon } },
128
+ { item: { id: 'wason', type: ITEM_TYPES.skin } },
129
+ { item: { id: 'scp-2040', type: ITEM_TYPES.skin } },
130
+ { item: { id: 'purple', type: ITEM_TYPES.skin } },
131
+ { item: { id: 'punk', type: ITEM_TYPES.skin } },
132
+ { item: { id: 'lain', type: ITEM_TYPES.skin } },
133
+ { item: { id: 'kaneki', type: ITEM_TYPES.skin } },
134
+ { item: { id: 'junko', type: ITEM_TYPES.skin } },
135
+ { item: { id: 'ghost', type: ITEM_TYPES.skin } },
136
+ { item: { id: 'eiri', type: ITEM_TYPES.skin } },
137
+ { item: { id: 'anon', type: ITEM_TYPES.skin } },
138
+ { item: { id: 'alex', type: ITEM_TYPES.skin } },
139
+ { item: { id: 'agent', type: ITEM_TYPES.skin } },
140
+ { item: { id: 'grass', type: ITEM_TYPES.floor } },
141
+ { item: { id: 'wood-1', type: ITEM_TYPES.resource } },
142
+ { item: { id: 'wood-2', type: ITEM_TYPES.resource } },
143
+ { item: { id: 'wood-extracted-1', type: ITEM_TYPES.resource } },
144
+ { item: { id: 'wood-extracted-2', type: ITEM_TYPES.resource } },
145
+ { item: { id: 'wood-drop-1', type: ITEM_TYPES.resource } },
146
+ { item: { id: 'wood-drop-2', type: ITEM_TYPES.resource } },
147
+ ];
148
+
149
+ const _ITEM_BY_ID = Object.freeze(
150
+ DefaultCyberiaItems.reduce((acc, entry) => {
151
+ acc[entry.item.id] = entry;
152
+ return acc;
153
+ }, {}),
154
+ );
155
+
156
+ /** O(1) lookup: item id → registry entry, or `null` if unknown. */
157
+ export const getDefaultCyberiaItemById = (itemId) => _ITEM_BY_ID[itemId] || null;
158
+
159
+ /** All registry entries of a given item type. */
160
+ export const getDefaultCyberiaItemsByItemType = (itemType) =>
161
+ DefaultCyberiaItems.filter((entry) => entry.item.type === itemType);
162
+
163
+ /** All registry entries whose item type is permitted on a given entity type. */
164
+ export const getDefaultCyberiaItemsByEntityType = (entityType) => {
165
+ const allowed = ENTITY_TYPE_TO_ITEM_TYPES[entityType] || [];
166
+ return DefaultCyberiaItems.filter((entry) => allowed.includes(entry.item.type));
167
+ };
168
+
169
+ // ─────────────────────────────────────────────────────────────────────────────
170
+ // Presentation defaults
171
+ // ─────────────────────────────────────────────────────────────────────────────
172
+
173
+ /**
174
+ * Named colour palette. Solid-color fallbacks for entity types, plus
175
+ * UI-only entries (WEAPON, SELF_BORDER) the renderer uses for HUD.
176
+ *
177
+ * Keys are stable identifiers shared with the C client's local palette.
178
+ * Adding a new key here requires adding it to
179
+ * `cyberia-client/src/domain/presentation_defaults.h` to keep parity.
180
+ *
181
+ * @type {ReadonlyArray<{key:string,r:number,g:number,b:number,a:number}>}
182
+ */
183
+ export const PALETTE = Object.freeze([
184
+ { key: 'BACKGROUND', r: 30, g: 30, b: 30, a: 255 },
185
+ { key: 'FLOOR_BACKGROUND', r: 45, g: 45, b: 45, a: 255 },
186
+ { key: 'FLOOR', r: 60, g: 60, b: 60, a: 255 },
187
+ { key: 'OBSTACLE', r: 80, g: 80, b: 80, a: 255 },
188
+ { key: 'PORTAL', r: 0, g: 200, b: 200, a: 255 },
189
+ { key: 'PORTAL_INTER_PORTAL', r: 0, g: 200, b: 200, a: 255 },
190
+ { key: 'PORTAL_INTER_RANDOM', r: 80, g: 130, b: 255, a: 255 },
191
+ { key: 'PORTAL_INTRA_RANDOM', r: 220, g: 200, b: 50, a: 255 },
192
+ { key: 'PORTAL_INTRA_PORTAL', r: 200, g: 80, b: 200, a: 255 },
193
+ { key: 'FOREGROUND', r: 255, g: 255, b: 255, a: 189 },
194
+ { key: 'PLAYER', r: 0, g: 255, b: 0, a: 255 },
195
+ { key: 'OTHER_PLAYER', r: 128, g: 128, b: 255, a: 255 },
196
+ { key: 'BOT', r: 255, g: 128, b: 0, a: 255 },
197
+ { key: 'GHOST', r: 200, g: 200, b: 255, a: 100 },
198
+ { key: 'COIN', r: 255, g: 215, b: 0, a: 255 },
199
+ { key: 'SKILL', r: 255, g: 255, b: 50, a: 255 },
200
+ { key: 'RESOURCE', r: 100, g: 180, b: 80, a: 255 },
201
+ { key: 'WEAPON', r: 180, g: 50, b: 50, a: 255 },
202
+ { key: 'SELF_BORDER', r: 220, g: 190, b: 60, a: 240 },
203
+ ]);
204
+
205
+ /**
206
+ * Per-entity-type palette key. Tells the client which palette entry to
207
+ * use as the solid-colour fallback for an entity that has no active
208
+ * ObjectLayer items.
209
+ */
210
+ export const ENTITY_COLOR_KEYS = Object.freeze([
211
+ { entityType: 'player', colorKey: 'PLAYER' },
212
+ { entityType: 'other_player', colorKey: 'OTHER_PLAYER' },
213
+ { entityType: 'bot', colorKey: 'BOT' },
214
+ { entityType: 'skill', colorKey: 'SKILL' },
215
+ { entityType: 'coin', colorKey: 'COIN' },
216
+ { entityType: 'floor', colorKey: 'FLOOR' },
217
+ { entityType: 'obstacle', colorKey: 'OBSTACLE' },
218
+ { entityType: 'portal', colorKey: 'PORTAL' },
219
+ { entityType: 'foreground', colorKey: 'FOREGROUND' },
220
+ { entityType: 'resource', colorKey: 'RESOURCE' },
221
+ ]);
222
+
223
+ /**
224
+ * Camera and render-tuning defaults. Pure presentation — the cyberia-server
225
+ * never reads any of these. The cyberia-client fetches the whole bundle
226
+ * from /api/cyberia-client-hints/:CYBERIA_CLIENT_HINTS_CODE at startup
227
+ * and treats this object as the on-disk schema.
228
+ *
229
+ * cellSize — pixels per simulation cell on the client viewport.
230
+ * Authoritative server works in cell units; this value
231
+ * only governs how cells map to pixels for rendering.
232
+ * interpolationMs — render-time smoothing window for remote entities.
233
+ * Server ticks at TickRate, snapshots at SnapshotRate,
234
+ * client decides how to display the stream.
235
+ * defaultObjWidth /
236
+ * defaultObjHeight — default entity dimensions used by the world editor
237
+ * and the client when a doc omits dims. Presentation
238
+ * because dims-in-cells is just a visual sizing.
239
+ */
240
+ export const RENDER_DEFAULTS = Object.freeze({
241
+ cellSize: 45,
242
+ defaultObjWidth: 1,
243
+ defaultObjHeight: 1,
244
+ cameraSmoothing: 0.1,
245
+ cameraZoom: 1.0,
246
+ defaultWidthScreenFactor: 1,
247
+ defaultHeightScreenFactor: 1,
248
+ interpolationMs: 100,
249
+ devUi: false,
250
+ });
251
+
252
+ /**
253
+ * Status-icon presentation half. The numeric `id` is shared with the
254
+ * simulation (it travels on the wire as a u8 inside the AOI binary
255
+ * entity-status indicator field). Everything else here — icon filename,
256
+ * border colour, bounce animation — is purely cosmetic and resolved on
257
+ * the client.
258
+ *
259
+ * IDs MUST stay aligned with `STATUS_ICONS` in `cyberia-server-defaults.js`.
260
+ */
261
+ export const STATUS_ICONS_PRESENTATION = Object.freeze([
262
+ { id: 0, iconId: null, bounce: false, borderColor: { r: 70, g: 70, b: 120, a: 200 } },
263
+ { id: 1, iconId: 'arrow-down-gray', bounce: false, borderColor: { r: 130, g: 140, b: 160, a: 200 } },
264
+ { id: 2, iconId: 'arrow-down-red', bounce: true, borderColor: { r: 210, g: 50, b: 50, a: 240 } },
265
+ { id: 3, iconId: 'chat', bounce: true, borderColor: { r: 80, g: 160, b: 220, a: 240 } },
266
+ { id: 4, iconId: 'arrow-down', bounce: false, borderColor: { r: 60, g: 190, b: 90, a: 240 } },
267
+ { id: 5, iconId: 'skull', bounce: false, borderColor: { r: 160, g: 130, b: 200, a: 200 } },
268
+ { id: 6, iconId: 'arrow-down-gray', bounce: false, borderColor: { r: 100, g: 180, b: 80, a: 220 } },
269
+ { id: 7, iconId: 'clock', bounce: false, borderColor: { r: 160, g: 130, b: 200, a: 200 } },
270
+ { id: 8, iconId: 'chat', bounce: true, borderColor: { r: 220, g: 190, b: 60, a: 240 } },
271
+ ]);
272
+
273
+ /**
274
+ * Build the full client-hints document for a given instance.
275
+ *
276
+ * @param {Object} [overrides] DB document fragments that may override
277
+ * palette colours, render defaults, status-icon iconIds, etc.
278
+ * Anything missing falls back to the canonical defaults exported here.
279
+ * @returns {Object} JSON-friendly client hints object.
280
+ */
281
+ export function buildClientHints(overrides = {}) {
282
+ const ov = overrides || {};
283
+
284
+ // Palette merge: canonical first, DB overrides keyed by name, append any
285
+ // truly new keys at the end.
286
+ const dbColors = new Map((ov.colors || []).map((c) => [c.key, c]));
287
+ const palette = PALETTE.map((c) => {
288
+ const o = dbColors.get(c.key);
289
+ return o ? { key: c.key, r: o.r ?? c.r, g: o.g ?? c.g, b: o.b ?? c.b, a: o.a ?? c.a } : { ...c };
290
+ });
291
+ for (const [key, c] of dbColors) {
292
+ if (!palette.some((p) => p.key === key)) {
293
+ palette.push({ key, r: c.r ?? 0, g: c.g ?? 0, b: c.b ?? 0, a: c.a ?? 255 });
294
+ }
295
+ }
296
+
297
+ // Status icons: only iconId is overridable. Border colour stays canonical
298
+ // because the DB schema defaults for that field are placeholder values.
299
+ const dbIcons = new Map((ov.statusIcons || []).map((s) => [s.id, s]));
300
+ const statusIcons = STATUS_ICONS_PRESENTATION.map((canon) => {
301
+ const o = dbIcons.get(canon.id);
302
+ return {
303
+ id: canon.id,
304
+ iconId: (o && o.iconId) || canon.iconId || '',
305
+ bounce: canon.bounce,
306
+ borderColor: { ...canon.borderColor },
307
+ };
308
+ });
309
+
310
+ return {
311
+ palette,
312
+ entityColorKeys: ENTITY_COLOR_KEYS.map((e) => ({ ...e })),
313
+ statusIcons,
314
+ cellSize: ov.cellSize ?? RENDER_DEFAULTS.cellSize,
315
+ defaultObjWidth: ov.defaultObjWidth ?? RENDER_DEFAULTS.defaultObjWidth,
316
+ defaultObjHeight: ov.defaultObjHeight ?? RENDER_DEFAULTS.defaultObjHeight,
317
+ cameraSmoothing: ov.cameraSmoothing ?? RENDER_DEFAULTS.cameraSmoothing,
318
+ cameraZoom: ov.cameraZoom ?? RENDER_DEFAULTS.cameraZoom,
319
+ defaultWidthScreenFactor: ov.defaultWidthScreenFactor ?? RENDER_DEFAULTS.defaultWidthScreenFactor,
320
+ defaultHeightScreenFactor: ov.defaultHeightScreenFactor ?? RENDER_DEFAULTS.defaultHeightScreenFactor,
321
+ interpolationMs: ov.interpolationMs ?? RENDER_DEFAULTS.interpolationMs,
322
+ devUi: ov.devUi ?? RENDER_DEFAULTS.devUi,
323
+ };
324
+ }
325
+
326
+ /**
327
+ * Canonical hints document — what the client gets when no DB overrides
328
+ * exist (or the engine endpoint is unreachable).
329
+ */
330
+ export const CYBERIA_CLIENT_HINTS_DEFAULTS = buildClientHints({});
@@ -8,7 +8,7 @@
8
8
 
9
9
  The Action System defines how NPC entities interact with players. An **Action** is a spatial, typed payload attached to a map entity that the player activates by tapping the NPC. Actions drive dialogue, shops, crafting, storage, and quest grant events.
10
10
 
11
- > **Implementation status — Pre-alpha:** The CyberiaAction and CyberiaDialogue MongoDB schemas and Engine REST API (`src/api/cyberia-action`, `src/api/cyberia-dialogue`) are defined. Go server integration (NPC tap routing to action handlers, shop/craft transaction processing, quest grant on dialogue completion) is planned for the **Alpha milestone**. The `freeze_start`/`freeze_end` WS messages for modal protection are implemented in the Go server today.
11
+ > **Implementation status — Alpha (talk / quest-talk):** The CyberiaAction and CyberiaDialogue MongoDB schemas and Engine REST API (`src/api/cyberia-action`, `src/api/cyberia-dialogue`) are defined. The `talk` and `quest-talk` paths are wired end-to-end: the Go server binds actions to entities at instance init, validates dialogue completion, grants quests, and advances `talk` objectives (see **Dialogue Interaction Protocol** below). Shop / craft / storage transaction processing remains planned for a later Alpha increment. The `freeze_start`/`freeze_end` WS messages for modal protection are implemented; dialogue freeze now rides on the `dlg_*` frames.
12
12
 
13
13
  ---
14
14
 
@@ -154,6 +154,60 @@ sequenceDiagram
154
154
 
155
155
  ---
156
156
 
157
+ ## Dialogue Interaction Protocol (talk / quest-talk)
158
+
159
+ Tapping an interaction bubble opens the Raylib-native **`modal_interact`** modal
160
+ first — the general-purpose entry point. Its `[Talk]` tab (shown only when the
161
+ entity has dialogue) opens `modal_dialogue`; its `[Chat / Profile]` tab opens the
162
+ JS overlay with no freeze.
163
+
164
+ The client is identical for `talk` and `quest-talk`; the **server** branches after
165
+ `dlg_complete`. The client never declares the action type, quest code, or quest
166
+ dialogue codes — the server resolves the bound action from its own
167
+ `entityId → CyberiaAction` cache (bound at instance init).
168
+
169
+ ### Wire messages
170
+
171
+ | Direction | Message | When | Payload |
172
+ | --------- | -------------- | ------------------------------------- | ------------------------------------ |
173
+ | C → S | `dlg_start` | `modal_dialogue` opens | `{ entityId, itemId }` |
174
+ | C → S | `dlg_complete` | player reads all lines, closes | `{ entityId, itemId, dialogCode }` |
175
+ | C → S | `dlg_cancel` | player dismisses early (✕ / outside) | `{ entityId, itemId }` |
176
+ | S → C | `dlg_ack` | after `dlg_complete` is processed | `{ questGranted, objectivesDone, quests[] }` |
177
+
178
+ Binary uplink opcodes: `dlg_start` `0x17`, `dlg_complete` `0x18`, `dlg_cancel`
179
+ `0x19` (JSON aliases of the same names are also accepted).
180
+
181
+ `dlg_ack` is notify-only — it carries the affected quest snapshot entries the
182
+ client upserts into its local `quest_store` (Quest Journal); it never gates
183
+ simulation state.
184
+
185
+ ### Server `dlg_complete` handling
186
+
187
+ 1. Validate `player.activeDialogueEntityID == msg.entityId`; drop otherwise.
188
+ 2. Clear the dialogue context and thaw the player (modal protection off).
189
+ 3. Resolve the action from `actionCache[entityId]`. `talk` → ack only.
190
+ 4. `quest-talk`: on first contact grant `grantQuestCode`; then for every active
191
+ quest whose **current step** has a `{ type: 'talk', itemId == provideItemId }`
192
+ objective, increment it — **only** when `msg.dialogCode` is in the action's
193
+ `questDialogueCodes`. On quest completion, deliver rewards (FCT) and unlock
194
+ successors.
195
+
196
+ > **Dialogue-code contract.** The C client fetches dialogue groups at
197
+ > `/api/cyberia-dialogue/code/default-<itemId>`, so the code it reports on
198
+ > `dlg_complete` is `default-<provideItemId>`. For a `quest-talk` objective to
199
+ > advance, the action's `questDialogueCodes` must contain that code.
200
+
201
+ ### Freeze semantics
202
+
203
+ | Event | Player state |
204
+ | ---------------------------------- | ------------------------- |
205
+ | `modal_interact` open | Active (no freeze) |
206
+ | `dlg_start` sent | Frozen — immune to damage |
207
+ | `dlg_complete` / `dlg_cancel` sent | Unfrozen |
208
+
209
+ ---
210
+
157
211
  ## Spatial Binding and Instance Init
158
212
 
159
213
  `sourceMapCode + sourceCellX + sourceCellY` links an Action to a specific entity cell in a specific map. During instance initialization: