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
@@ -1,269 +1,252 @@
1
- # Cyberia Client
1
+ <p align="center">
2
+ <img src="https://www.cyberiaonline.com/assets/splash/apple-touch-icon-precomposed.png" alt="CYBERIA online"/>
3
+ </p>
2
4
 
3
- **Path:** `cyberia-client/` | **Language:** C (C11/GNU11) → WebAssembly via Emscripten
5
+ <div align="center">
4
6
 
5
- ---
7
+ <h1>cyberia client</h1>
8
+
9
+ </div>
10
+
11
+ **Path:** `cyberia-client/` · **Language:** C11/GNU11 → WebAssembly (Emscripten) · **Role:** presentation runtime for Cyberia
6
12
 
7
- ## Overview
13
+ `cyberia-client` is the rendering and interactive runtime for the Cyberia MMO extension on Underpost Platform. It captures input, predicts the local player, reconciles against authoritative snapshots, interpolates remote entities, and renders the world. It owns the render policy locally.
8
14
 
9
- `cyberia-client` is the real-time game client for Cyberia Online. It is written in C, compiled to WebAssembly via Emscripten, and rendered in a `<canvas>` element inside a browser. It uses **Raylib** for rendering (OpenGL ES2 under WASM) and connects to the Go game server via a persistent binary WebSocket.
15
+ It is **not** a world-simulation authority. The authoritative simulation runs on `cyberia-server`. Persistent content and asset metadata come from `engine-cyberia` via REST.
10
16
 
11
17
  ---
12
18
 
13
- ## Architecture
19
+ ## Operating model
14
20
 
21
+ Three independent processes, non-overlapping roles. The ecosystem is playable only when all three are running and healthy at the same time.
22
+
23
+ ```text
24
+ engine-cyberia cyberia-server cyberia-client
25
+ content authority simulation authority presentation runtime
26
+ ^ REST | WebSocket binary
27
+ | atlases, asset | snapshots (server -> client)
28
+ | metadata, optional | input cmds (client -> server)
29
+ | client hints v
30
+ `---------------- Browser tab: prediction, reconciliation, interpolation, render
15
31
  ```
16
- ┌────────────────────────────────────────────────────────────┐
17
- │ Browser │
18
- │ ┌──────────────────────────────────────────────────────┐ │
19
- │ │ index.html + shell.html │ │
20
- │ │ JS glue (Emscripten + services.js + interact_overlay.js + notify_badge.js) │
21
- │ │ │ │
22
- │ │ ┌────────────────────────────────────────────────┐ │ │
23
- │ │ │ index.wasm (main.c game loop) │ │ │
24
- │ │ │ │ │ │
25
- │ │ │ Raylib (OpenGL ES2) ← game_render.c │ │ │
26
- │ │ │ Input system ← input.c │ │ │
27
- │ │ │ AOI decoder ← binary_aoi_decoder.c │ │ │
28
- │ │ │ ObjectLayer cache ← object_layer.c │ │ │
29
- │ │ │ Texture manager ← texture_manager.c │ │ │
30
- │ │ │ Game state ← game_state.c │ │ │
31
- │ │ │ Network I/O ← network.c │ │ │
32
- │ │ │ UI components ← modal.c, inventory_*.c│ │ │
33
- │ │ └────────────────────────────────────────────────┘ │ │
34
- │ └──────────────────────────────────────────────────────┘ │
35
- │ │ WebSocket binary ↑ HTTP JSON │
36
- └───────┼─────────────────────────────────┼──────────────────┘
37
- │ │
38
- ▼ ▼
39
- Go Game Server :8081 Engine REST API :4005
40
- (AOI binary protocol) (atlas, file/blob, object-layer)
41
- ```
32
+
33
+ - Each service owns its own monitor and reconnector.
34
+ - The client reconnects to `cyberia-server` over WebSocket and fetches content from `engine-cyberia` over REST.
35
+ - If any of the three services is unhealthy, the game moves to standby until all three recover.
36
+
37
+ The client speaks two transports:
38
+
39
+ - **WebSocket binary** to `cyberia-server` for snapshots and input commands
40
+ - **REST** to `engine-cyberia` for atlas frames, asset blobs, and optional client-hints overrides
41
+
42
+ Asset distribution flows through Underpost Platform's static and PWA pipeline.
42
43
 
43
44
  ---
44
45
 
45
- ## Source Files
46
-
47
- | File | Responsibility |
48
- | --------------------------------------------------------- | -------------------------------------------------------- |
49
- | `main.c` | Entry point, Raylib init, main game loop, event dispatch |
50
- | `client.c / client.h` | Client session state (player UUID, position, coins) |
51
- | `game_state.c / game_state.h` | World state: entity registry, map grid, frame tick |
52
- | `game_render.c / game_render.h` | Top-level render pipeline (map tiles entities UI) |
53
- | `entity_render.c / entity_render.h` | Per-entity composited layer stack rendering |
54
- | `binary_aoi_decoder.c / binary_aoi_decoder.h` | Binary AOI message parser (0x01–0x05 messages) |
55
- | `message_parser.c / message_parser.h` | JSON message parser for `init_data` (0x02) |
56
- | `network.c / network.h` | WebSocket connect, send, receive wrappers |
57
- | `object_layer.c / object_layer.h` | ObjectLayer metadata struct + LRU cache |
58
- | `object_layers_management.c / object_layers_management.h` | ObjectLayer fetch pipeline (API → cache → render) |
59
- | `texture_manager.c / texture_manager.h` | Atlas texture LRU cache keyed by IPFS CID |
60
- | `layer_z_order.c / layer_z_order.h` | Z-order comparator for entity layer stack rendering |
61
- | `serial.c / serial.h` | IDBFS-based persistent local storage |
62
- | `input.c / input.h` | Mouse/touch event → game action translation |
63
- | `tap_effect.c / tap_effect.h` | Visual tap ripple animation |
64
- | `floating_combat_text.c / floating_combat_text.h` | FCT pop-up renderer (damage, regen, coin, item) |
65
- | `interaction_bubble.c / interaction_bubble.h` | NPC interaction indicator bubble above entity |
66
- | `entity_overhead_ui.c / entity_overhead_ui.h` | Name plates, HP bars above entities |
67
- | `nameplate.c / nameplate.h` | Entity nameplate rendering |
68
- | `inventory_bar.c / inventory_bar.h` | Equipped items bar (bottom of screen) |
69
- | `inventory_modal.c / inventory_modal.h` | Full inventory grid modal |
70
- | `modal.c / modal.h` | Base modal container (backdrop, open/close animation) |
71
- | `modal_dialogue.c / modal_dialogue.h` | Dialogue modal — renders NPC conversation + choices |
72
- | `modal_player.c / modal_player.h` | Player profile modal (stats, wallet, equipment) |
73
- | `dialogue_data.c / dialogue_data.h` | Dialogue tree data structure |
74
- | `ol_as_animated_ico.c / ol_as_animated_ico.h` | Render ObjectLayer as animated icon (inventory cells) |
75
- | `ol_stack_ico.c / ol_stack_ico.h` | Render ObjectLayer stack as composite icon |
76
- | `ui_icon.c / ui_icon.h` | Generic icon primitives |
77
- | `dev_ui.c / dev_ui.h` | Development overlay (coords, FPS, entity count) |
78
- | `helper.h` | Common macros and utility definitions |
79
- | `config.h` | Compile-time configuration constants |
80
- | `shell.html` | Emscripten HTML shell template |
81
- | `js/services.js` | JS↔WASM bridge: fetch atlas, file blob, object-layer |
82
- | `js/interact_overlay.js` | JS overlay for tap-to-interact element |
83
- | `js/notify_badge.js` | Browser notification badge helper |
46
+ ## Render loop
47
+
48
+ The render loop runs at vsync. Tick simulation is decoupled from render frames via a fixed-timestep accumulator. Step ordering inside one render frame:
49
+
50
+ 1. **Optional client hints poll** advance the asynchronous hints fetch state machine.
51
+ 2. **Input capture** drain raw OS events.
52
+ 3. **Input dispatch** — UI hit-test → build typed input command (`kind`, `clientTick`, `sequence`, payload) apply to prediction send on the wire.
53
+ 4. **Reconciliation** — apply latest snapshot's `lastAcked`: drop acknowledged input commands from the replay buffer; rewind predicted self to authoritative position; replay unacked input commands.
54
+ 5. **Fixed-timestep prediction** — while accumulator ≥ `tickDuration`, advance prediction one tick.
55
+ 6. **Interpolation** compute remote-entity view positions at `renderTick = serverTickEstimate − INTERP_TICKS`.
56
+ 7. **Render** read view models; never mutate world state.
57
+
58
+ Render frame rate is independent of simulation tick rate. The fixed-timestep accumulator ensures the predicted simulation advances at the authoritative `tickRate` regardless of FPS.
84
59
 
85
60
  ---
86
61
 
87
- ## Rendering Pipeline
62
+ ## Module map
88
63
 
89
- Each frame:
64
+ Directional dependency: from outermost (UI / render) inward toward `domain/`. Nothing in `domain/` imports outward.
90
65
 
91
66
  ```
92
- BeginDrawing()
93
- └─ game_render_frame()
94
- ├─ RenderMapLayer(ground tiles) ← atlas-clipped tile sprites
95
- ├─ RenderMapLayer(object tiles) ← decorative overlay tiles
96
- ├─ for each entity in z-sorted list:
97
- │ entity_render_entity()
98
- │ ├─ for each ObjectLayer in entity stack (bottom → top z-order):
99
- │ │ texture_manager_get_texture(layer.cid)
100
- │ │ DrawTextureRec(atlas, frame_rect, position)
101
- │ ├─ entity_overhead_ui (nameplate + HP bar)
102
- │ └─ interaction_bubble (if NPC with action)
103
- ├─ floating_combat_text_tick() ← animated pop-ups
104
- ├─ tap_effect_tick() ← tap ripple animations
105
- ├─ inventory_bar_render() ← equipped items (bottom HUD)
106
- └─ dev_ui_render() ← debug overlay (if ENABLE_DEV_UI)
107
- EndDrawing()
67
+ src/
68
+ domain/
69
+ tick.h tick rate constants, monotonic types
70
+ presentation_runtime.{c,h} sole owner of presentation: async fetch of
71
+ /api/cyberia-client-hints/:CYBERIA_CLIENT_HINTS_CODE,
72
+ palette/status-icon/camera accessors, tiny
73
+ inline bootstrap fallback
74
+ input/
75
+ input_command.{c,h} typed InputCommand factory; sequence allocation
76
+ prediction/
77
+ prediction.{c,h} predicted self position; replay buffer; reconcile
78
+ interpolation/
79
+ interpolation.{c,h} remote entity view positions at renderTick
80
+ network/
81
+ session.{c,h} last server tick, last acked sequence, tick estimate
82
+ client.{c,h} WebSocket lifecycle + binary I/O
83
+ socket.{c,h} Emscripten WebSocket bridge
84
+ ui/ modals, HUD, inventory bar, FCT, nameplates
85
+ game_state.{c,h} world state mirror (gameplay subset only)
86
+ binary_aoi_decoder.{c,h} server snapshot parser
87
+ game_render.{c,h}, render.{c,h} render pipeline
88
+ main.c entry point + render loop
89
+ js/services.{js,c,h} REST fetch bridge (atlas, ui-icons, client-hints)
108
90
  ```
109
91
 
110
- **Atlas clipping:** Every sprite is a rectangular clip from a texture atlas. The atlas CID (IPFS) is fetched from the Engine API at `/api/atlas-sprite-sheet/{cid}`. The engine returns frame metadata JSON; the client clips `DrawTextureRec(atlas, {x,y,w,h}, screenPos)`.
92
+ | Owner | Owns | Reads | Writes |
93
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- |
94
+ | `domain/presentation_runtime` | the entire presentation surface (palette, entity colour keys, status icons, camera, cell, interpolation). Inline bootstrap fallback while the fetch is in flight. | JSON response from `/api/cyberia-client-hints` | own table + one-shot hydration of `g_game_state.cell_size`, `.interpolation_ms`, `.camera.zoom` |
95
+ | `input/input_command` | typed InputCommand factory | session for tick + sequence | — |
96
+ | `prediction/` | predicted self position, input replay buffer | snapshot self + session ack | predicted self |
97
+ | `interpolation/` | remote entity `interp_pos` | snapshot history | remote `interp_pos` only |
98
+ | `network/session` | last server tick, last acked sequence | snapshot header | session singletons |
99
+ | `binary_aoi_decoder` | wire parser | binary frames | `game_state` + session + prediction (via callback) |
100
+ | `game_state` | gameplay world mirror | — | — |
101
+ | `render/`, `ui/` | rendering | view models | screen |
102
+
103
+ `game_state` holds only the gameplay subset of world state (entities, positions, life, AOI, equipment, frozen flag, coins). It does not carry palette state, status-icon visuals, or any other presentation field.
111
104
 
112
105
  ---
113
106
 
114
- ## Binary AOI Protocol (Decoder)
107
+ ## Presentation ownership
115
108
 
116
- `binary_aoi_decoder.c` parses all server client messages:
109
+ The client owns its render policy. The authoritative server holds no presentation state.
117
110
 
118
- | Message Type | Byte | Decoder Function | Description |
119
- | ------------ | ------ | ------------------- | ----------------------------------------- |
120
- | `aoi_update` | `0x01` | `decode_aoi_update` | Delta AOI — entities entered/left view |
121
- | `init_data` | `0x02` | `decode_init_data` | Full config on connect (JSON sub-payload) |
122
- | `full_aoi` | `0x03` | `decode_full_aoi` | Full AOI snapshot |
123
- | `FCT` | `0x04` | `decode_fct` | Floating Combat Text (14-byte fixed) |
124
- | `ItemFCT` | `0x05` | `decode_item_fct` | Item FCT with string item ID (variable) |
111
+ Three layers, strictly inward-dependent:
125
112
 
126
- **All multi-byte integers: little-endian.**
113
+ 1. **`domain/presentation_runtime.{c,h}`** — sole owner of every presentation value. Fires a single asynchronous GET against `/api/cyberia-client-hints/:CYBERIA_CLIENT_HINTS_CODE` at startup, polled once per render frame. When the response settles, parses palette, entity colour keys, status-icon visuals, and camera/cell tunings into a process-local table and writes a one-shot hydration into `g_game_state` (cell_size, interpolation_ms, camera.zoom). The C client carries NO compile-time palette or status table — only a tiny inline neutral-grey bootstrap so the splash screen has something to draw while the fetch is in flight. The canonical schema for the response lives at engine-cyberia's `src/client/components/cyberia/SharedDefaultsCyberia.js`.
114
+ 2. **Renderers** — call `presentation_runtime_palette("KEY")`, `presentation_runtime_status_icon(u8)`, `presentation_runtime_status_border(u8)`, `presentation_runtime_entity_fallback_color(entity_type)` at each use site.
127
115
 
128
- **FCT type visual:**
116
+ What lives in this layer:
129
117
 
130
- | FCT Type | Byte | Color |
131
- | -------- | ------ | ------ |
132
- | Damage | `0x00` | Red |
133
- | Regen | `0x01` | Green |
134
- | CoinGain | `0x02` | Yellow |
135
- | CoinLoss | `0x03` | Yellow |
136
- | ItemGain | `0x04` | Cyan |
137
- | ItemLoss | `0x05` | Purple |
118
+ - Named palette (colors).
119
+ - Per-entity-type fallback color keys.
120
+ - Status-icon visual table (icon stem + border color per numeric ID).
121
+ - Camera smoothing / zoom defaults.
122
+ - Interpolation window.
123
+ - Dev-overlay flag.
124
+ - Viewport screen factors.
138
125
 
139
- ---
126
+ What does **not** travel on the WebSocket init or AOI streams:
140
127
 
141
- ## ObjectLayer Cache
128
+ - Palette.
129
+ - Camera knobs.
130
+ - Dev-overlay flag.
131
+ - Interpolation window.
132
+ - Status-icon visuals.
133
+ - Per-entity-type color keys.
142
134
 
143
- `object_layer.c` maintains an LRU cache (`MAX_LAYER_CACHE_SIZE = 256`) of parsed `ObjectLayerMetadata` structs:
144
-
145
- ```
146
- Fetch pipeline per item ID:
147
- 1. Check LRU cache → hit? return immediately
148
- 2. Call JS bridge: services.fetchObjectLayer(itemId)
149
- → GET {API_BASE_URL}/api/object-layer/{itemId}
150
- 3. Parse JSON response → populate ObjectLayerMetadata
151
- 4. Evict LRU entry if cache full → insert new entry
152
- 5. Trigger texture fetch for each layer CID
153
- ```
135
+ The numeric status-icon u8 still rides on the AOI wire — that is the protocol-level half. The icon stem and border color are presentation and are resolved entirely on the client.
154
136
 
155
- ---
137
+ ### Optional client hints
156
138
 
157
- ## Texture Manager
139
+ `GET /api/cyberia-client-hints/:instanceCode` (engine-cyberia REST, **not** cyberia-server) returns a JSON document mirroring the structure of the compile-time defaults. The client is required to function with no successful call to this endpoint:
158
140
 
159
- `texture_manager.c` maintains an LRU cache (`MAX_TEXTURE_CACHE_SIZE = 512`) of Raylib `Texture2D` objects:
141
+ - 200 with `{ palette, entityColorKeys, statusIcons, cameraSmoothing, cameraZoom, defaultWidthScreenFactor, defaultHeightScreenFactor, interpolationMs, devUi }` — overrides applied on top of defaults.
142
+ - 404 if the instance has no overrides — defaults are used.
143
+ - Network error — defaults are used.
160
144
 
161
- ```
162
- Fetch pipeline per CID:
163
- 1. Check cache by CID → hit? return Texture2D*
164
- 2. Call JS bridge: services.fetchFileBlob(cid)
165
- → GET {API_BASE_URL}/api/file/blob/{cid}
166
- 3. Decode PNG blob → Raylib Image → Texture2D (GPU upload)
167
- 4. Evict LRU on full cache → insert new entry
168
- ```
145
+ No authentication; presentation hints are not secret. The Go authoritative server never calls this endpoint.
169
146
 
170
147
  ---
171
148
 
172
- ## Input System (Tap-Based)
149
+ ## Tick-aware components
173
150
 
174
- The client uses a **tap-first** interaction model:
151
+ ### Session
175
152
 
176
- | Input | Action |
177
- | ------------------------------- | ------------------------------------------------------------ |
178
- | Tap empty tile | Send `tap_move` WS message → player moves to cell |
179
- | Tap entity (NPC/resource) | Send `tap_entity` WS message → server dispatches interaction |
180
- | Tap inventory item | Toggle equip/unequip → send `item_activation` |
181
- | Tap interact overlay (JS layer) | Opens action modal via `_c_open_dialogue_from_js` |
153
+ `network/session.{c,h}` tracks per-connection tick state:
182
154
 
183
- `input.c` translates raw Raylib mouse/touch events into game-world cell coordinates using the current camera offset, then dispatches the appropriate WS message via `network_send_message`.
155
+ - `last_server_tick` highest snapshot tick observed.
156
+ - `last_acked_input_sequence` — highest sequence the server has applied for this client; echoed in every snapshot header.
157
+ - `next_input_sequence` — monotonic counter for outgoing input commands.
158
+ - `server_tick_estimate` — extrapolation of last server tick by elapsed wall time, used to stamp outgoing input commands and to compute `renderTick`.
184
159
 
185
- ---
160
+ ### Prediction
186
161
 
187
- ## JS↔WASM Bridge
162
+ `prediction/prediction.{c,h}` owns the predicted self position. It is the only writer of the predicted state. Internally it tracks **two** positions: the discrete per-tick `predicted_pos` (where the simulation says the player is) and the continuous render-frame `display_pos` (what the renderer reads). The renderer never sees the discrete value.
188
163
 
189
- Emscripten exported C functions (callable from JavaScript):
164
+ - `prediction_apply(cmd)` optimistic local apply of an input command + push onto replay buffer.
165
+ - `prediction_step(dt)` — fixed-timestep advance, called by the accumulator. Mutates `predicted_pos` only.
166
+ - `prediction_reconcile()` — on snapshot arrival: drop acknowledged inputs, rewind self to authoritative position, replay unacked inputs. Mutates `predicted_pos` only.
167
+ - `prediction_display_step(frame_dt)` — per-render-frame exponential lerp of `display_pos` toward `predicted_pos`. This is the layer that absorbs reconcile snaps and sim-tick stepping so the visible main player moves continuously.
168
+ - `prediction_self_position()` — render-time accessor; returns the smoothed `display_pos`.
190
169
 
191
- | C Function | Description |
192
- | --------------------------------- | --------------------------------------------- |
193
- | `_c_send_ws_message(ptr, len)` | Send raw bytes over the WebSocket |
194
- | `_c_open_dialogue_from_js(ptr)` | Open dialogue modal with serialized JSON |
195
- | `_c_interact_overlay_did_close()` | Notify WASM that the JS overlay was dismissed |
170
+ ### Interpolation
196
171
 
197
- JavaScript library functions (callable from C via `EM_ASM` / `--js-library`):
172
+ `interpolation/interpolation.{c,h}` owns remote-entity render-time positions. It is the only writer of remote `interp_pos`. It never touches the local player (prediction owns that). Sampling happens at `renderTick = serverTickEstimate − INTERP_TICKS`.
198
173
 
199
- | JS Function | Source | Description |
200
- | ----------------------------------------- | ------------------------ | -------------------------------------- |
201
- | `services.fetchObjectLayer(itemId, cb)` | `js/services.js` | Fetch ObjectLayer JSON from Engine API |
202
- | `services.fetchAtlasSpriteSheet(cid, cb)` | `js/services.js` | Fetch atlas frame metadata |
203
- | `services.fetchFileBlob(cid, cb)` | `js/services.js` | Fetch raw IPFS-addressed blob |
204
- | `interact_overlay.show(entityId)` | `js/interact_overlay.js` | Show tap-to-interact HTML overlay |
205
- | `notify_badge.set(count)` | `js/notify_badge.js` | Update browser notification badge |
174
+ ### Input command pipeline
175
+
176
+ ```
177
+ mouse / touch / keyboard
178
+
179
+
180
+ input/input_capture
181
+
182
+
183
+ UI hit-test cascade ──► modal/inventory/HUD consumed?
184
+ │ no
185
+
186
+ input_command_build_* stamps client_tick + sequence
187
+
188
+
189
+ prediction_apply optimistic local apply
190
+
191
+
192
+ uplink send WebSocket binary frame
193
+ ```
194
+
195
+ `InputCommand.Sequence` is allocated by `session_next_input_sequence`. Monotonic, never reused. The server echoes the highest applied sequence in every snapshot header; the prediction module drops acked commands from its replay buffer on reconciliation.
206
196
 
207
197
  ---
208
198
 
209
- ## Compile-Time Configuration (`config.h`)
199
+ ## Binary AOI snapshot format
200
+
201
+ The client decodes server-pushed AOI frames. Header (little-endian, 11 bytes for `0x01`/`0x03`):
210
202
 
211
- | Constant | Default | Description |
212
- | --------------------------- | ----------------------------------- | ---------------------------------------------- |
213
- | `WS_URL` | `wss://server.cyberiaonline.com/ws` | WebSocket endpoint |
214
- | `API_BASE_URL` | `https://www.cyberiaonline.com` | Engine REST API base URL |
215
- | `HTTP_TIMEOUT_SECONDS` | `10` | HTTP request timeout (asset fetching) |
216
- | `MAX_TEXTURE_CACHE_SIZE` | `512` | Max atlas textures in VRAM cache |
217
- | `MAX_LAYER_CACHE_SIZE` | `256` | Max ObjectLayer metadata entries in cache |
218
- | `MAX_ATLAS_CACHE_SIZE` | `256` | Max atlas sprite sheet metadata entries |
219
- | `DEFAULT_FRAME_DURATION_MS` | `100` | Default animation frame rate |
220
- | `ENABLE_DEV_UI` | `false` | Force dev overlay on (override server setting) |
221
- | `APP_VERSION` | `"1.0.0"` | Application version string |
203
+ ```
204
+ [0] u8 msgType 0x01 aoi_update | 0x03 full_aoi
205
+ [1..4] u32 tick simulation tick at which the snapshot was produced
206
+ [5..8] u32 lastAcked highest InputCommand.Sequence applied for this client
207
+ [9..10] u16 entityCount entity blocks that follow
208
+ ```
222
209
 
223
- For local development, change `WS_URL` and `API_BASE_URL` to `localhost` variants before rebuilding.
210
+ After parsing the header, the decoder invokes `session_on_snapshot(tick, lastAcked)` and then `prediction_reconcile()` so the predicted self stays consistent with the just-arrived authoritative state.
224
211
 
225
- ---
212
+ Other message types — init data (0x02), FCT (0x04), ItemFCT (0x05) — carry their own headers and are not part of the per-tick replication stream.
226
213
 
227
- ## Build System
214
+ ---
228
215
 
229
- ### Dependencies
216
+ ## REST fetches (engine-cyberia)
230
217
 
231
- | Library | Version | Usage |
232
- | ---------- | ------- | ----------------------------------- |
233
- | Emscripten | ≥ 3.1 | C → WASM compiler + JS runtime |
234
- | Raylib | ≥ 5.0 | Rendering (OpenGL ES2 via WebGL) |
235
- | cJSON | 1.7.x | JSON parsing (ObjectLayer metadata) |
218
+ The client speaks REST directly to engine-cyberia for content. None of these calls go through cyberia-server.
236
219
 
237
- Raylib and cJSON are vendored under `libs/`.
220
+ | Endpoint | Purpose |
221
+ | ------------------------------------------------ | ------------------------------------ |
222
+ | `GET /api/atlas-sprite-sheet/metadata/:itemKey` | Frame layout JSON for a sprite atlas |
223
+ | `GET /api/atlas-sprite-sheet/blob/:itemKey` | Atlas PNG |
224
+ | `GET /api/object-layer/:itemId` | ObjectLayer JSON metadata |
225
+ | `GET /api/cyberia-dialogue/code/default-:itemId` | Dialogue lines for an NPC |
226
+ | `GET /assets/ui-icons/:iconId.png` | Status-bar icons |
227
+ | `GET /api/cyberia-client-hints/:instanceCode` | Optional presentation overrides |
238
228
 
239
- ### Build Modes
229
+ All requests are CORS-simple GETs (no preflight) and cacheable. None require credentials.
240
230
 
241
- | Mode | Flags | Use |
242
- | ----------------- | -------------------- | ----------------------------------------------- |
243
- | `DEBUG` (default) | `-O0 -g --profiling` | Development — includes symbols, no optimization |
244
- | `RELEASE` | `-O3 -DNDEBUG` | Production — fully optimized |
231
+ ---
245
232
 
246
- ### Build Commands
233
+ ## Build and run
247
234
 
248
235
  ```bash
249
236
  cd cyberia-client
250
237
 
251
- # Debug build (default)
252
- make -f Web.mk
238
+ # Development build
239
+ make -f Web.mk clean && make -f Web.mk web
253
240
 
254
241
  # Release build
255
- make -f Web.mk BUILD_MODE=RELEASE
242
+ make -f Web.mk clean && make -f Web.mk web BUILD_MODE=RELEASE
256
243
 
257
244
  # Build + serve on dev port :8082
258
245
  make -f Web.mk serve-development
259
-
260
- # Build release + serve on production port :8081
261
- make -f Web.mk serve-production
262
-
263
- # Clean build artifacts
264
- make -f Web.mk clean
265
246
  ```
266
247
 
248
+ The build is part of the Underpost Platform static + PWA pipeline; production deploys go through `underpost client` and `underpost deploy`.
249
+
267
250
  ### Output
268
251
 
269
252
  ```
@@ -271,43 +254,38 @@ bin/
271
254
  index.html — Emscripten HTML container (from shell.html template)
272
255
  index.wasm — Compiled WebAssembly module
273
256
  index.js — Emscripten JS glue
274
- index.data — Preloaded data bundle (assets)
257
+ index.data — Preloaded data bundle
275
258
  ```
276
259
 
277
260
  ---
278
261
 
279
- ## Persistent Storage
262
+ ## Compile-time configuration
280
263
 
281
- `serial.c` uses Emscripten's **IDBFS** (IndexedDB File System) to persist:
264
+ `src/config.h`:
282
265
 
283
- - Player authentication token (EIP-712 signature)
284
- - Local game settings (audio volume, key bindings)
285
- - Cached ObjectLayer metadata (session cache warm-up)
266
+ | Constant | Default | Description |
267
+ | --------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
268
+ | `WS_URL` | `wss://server.cyberiaonline.com/ws` | WebSocket endpoint of cyberia-server |
269
+ | `API_BASE_URL` | `https://www.cyberiaonline.com` | engine-cyberia REST base URL |
270
+ | `CYBERIA_CLIENT_HINTS_CODE` | `cyberia-main` | Lookup key for the optional client-hints fetch (presentation override key — never an instance/server identifier) |
271
+ | `HTTP_TIMEOUT_SECONDS` | `10` | HTTP request timeout |
272
+ | `MAX_TEXTURE_CACHE_SIZE` | `512` | Atlas texture LRU cap |
273
+ | `MAX_LAYER_CACHE_SIZE` | `256` | ObjectLayer metadata LRU cap |
274
+ | `MAX_ATLAS_CACHE_SIZE` | `256` | Atlas metadata LRU cap |
275
+ | `DEFAULT_FRAME_DURATION_MS` | `100` | Default animation frame duration |
276
+ | `ENABLE_DEV_UI` | `false` | Force dev overlay regardless of presentation hints |
277
+ | `APP_VERSION` | `"1.0.0"` | Application version string |
286
278
 
287
- Data is stored at `/persistent/` in the virtual IDBFS mount.
279
+ For local development, point `WS_URL` and `API_BASE_URL` at `localhost` before rebuilding.
288
280
 
289
281
  ---
290
282
 
291
- ## Environment: Local Development
292
-
293
- Change these two constants in `src/config.h` and rebuild:
294
-
295
- ```c
296
- static const char* WS_URL = "ws://localhost:8081/ws";
297
- static const char* API_BASE_URL = "http://localhost:4005";
298
- ```
299
-
300
- Then:
283
+ ## Persistent storage
301
284
 
302
- ```bash
303
- # Terminal 1 — Engine (Node.js)
304
- cd /path/to/engine && node src/index.js
285
+ `serial.c` uses Emscripten's IDBFS (IndexedDB File System) at `/persistent/` for:
305
286
 
306
- # Terminal 2 Go game server
307
- cd cyberia-server && go run main.go
308
-
309
- # Terminal 3 — Client build + serve
310
- cd cyberia-client && make -f Web.mk serve-development
311
- ```
287
+ - Player authentication token (EIP-712 signature).
288
+ - Local game settings (audio volume, key bindings).
289
+ - Cached ObjectLayer metadata (session warm-up cache).
312
290
 
313
- Open `http://localhost:8082` in a browser.
291
+ No game state is persisted client-side; the authoritative server is the source of truth.