Files
wownerogue/docs/ARCHITECTURE.md
T
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

129 lines
7.5 KiB
Markdown

# Architecture
Wowngeon is a provably fair, block-timed roguelike with crypto entry and payouts. A solo run is a
race against a random block deadline; multiplayer matches run the same engine under a ruleset. The
game also acts as a multi-chain on-ramp for the Smirk wallet.
This page is the index for the architecture docs. Each subsystem has its own page.
## System map
```
┌──────────── players (browser) ─────────────┐
│ index.html · tavern.html · match.html │
│ html/js/render (render kit) │
└───────────────┬───────────────┬────────────┘
Socket.IO │ │ REST (auth, payments, status)
┌────────────────▼───────────────▼────────────┐
│ src/network/* socket handlers, chat, │
│ tavern, match, payments │
├─────────────────────────────────────────────┤
game core │ src/game/* dungeon · rulesets · modes │ → RULESETS.md
money │ src/payments/* providers · wallet-rpc │ → PAYMENTS.md
cosmetics │ src/multiplayer/entitlements + catalog │ → MONETIZATION.md
chains │ src/chain/chainProfile.js │ → chain profiles, below
chat │ src/network/chat/* local + nostr │ → CHAT_AND_NOSTR.md
└─────────────────────────────────────────────┘
│ │
PostgreSQL Wownero/Monero wallet-RPC, BTCPay/checkout gateways
```
`src/index.js` is the composition root: it builds the Express app and Socket.IO server, wires the
database manager, wallet RPC service, payment config, game mode manager and socket handlers, and
mounts the REST routes in `src/routes/`.
## Subsystem guide
| Area | Where | Doc |
|------|-------|-----|
| Credits, cosmetic catalog, entitlements | `src/multiplayer/entitlements.js`, `src/services/catalogService.js` | [MONETIZATION.md](MONETIZATION.md) |
| Chain constants for the five Smirk chains | `src/chain/chainProfile.js` | below |
| Payment providers and payouts | `src/payments/` | [PAYMENTS.md](PAYMENTS.md) |
| Rulesets and match engine | `src/game/rulesets/`, `src/multiplayer/MatchEngine.js` | [RULESETS.md](RULESETS.md), [MATCH_MODE.md](MATCH_MODE.md) |
| Tavern lobby and spectating | `src/network/tavernManager.js`, `src/multiplayer/Room.js` | [TAVERN_AND_MULTIPLAYER.md](TAVERN_AND_MULTIPLAYER.md) |
| Chat, local and nostr | `src/network/chat/` | [CHAT_AND_NOSTR.md](CHAT_AND_NOSTR.md) |
| Client render kit and art packs | `html/js/render/` | [RENDER_PACKS.md](RENDER_PACKS.md) |
| Balance and pacing | `src/sim/`, `src/game/difficultyConfig.js` | [BALANCE_SIM.md](BALANCE_SIM.md), [MULTI_LEVEL.md](MULTI_LEVEL.md) |
| Deployment and operations | `scripts/deploy/` | [DEPLOY.md](DEPLOY.md), [DEPLOY_INSTANCES.md](DEPLOY_INSTANCES.md), [LOGS_AND_BACKUP.md](LOGS_AND_BACKUP.md) |
| Financial reconciliation and exports | `src/services/` | [FINANCIAL_RECONCILIATION.md](FINANCIAL_RECONCILIATION.md), [FINANCIAL_EVENT_EXPORT.md](FINANCIAL_EVENT_EXPORT.md) |
Optional subsystems are environment-gated and dormant by default: payment gateways register only
when their `*_URL` / `*_STORE_ID` / `*_API_KEY` trio is set, global nostr chat requires
`NOSTR_CHAT_ENABLED=true`, and match rooms require `MATCH_ENABLED=true`. With none of those set the
server runs the native wallet-RPC payment path, local socket chat, and solo play only. The full
environment template is [`src/.env.example`](../src/.env.example).
## Chain profiles
`src/chain/chainProfile.js` is the single source of truth for per-chain constants across BTC, LTC,
XMR, WOW and GRIN: `decimalsFor`, `meanBlockTimeMsFor`, `familyFor` (`monero` / `utxo` /
`mimblewimble`), `uriSchemeFor` and `atomicDivisor` (BigInt).
| Chain | Decimals | Mean block time | Family | URI scheme |
|-------|----------|-----------------|--------|------------|
| WOW | 11 | 5 min | monero | `wownero` |
| XMR | 12 | 2 min | monero | `monero` |
| BTC | 8 | 10 min | utxo | `bitcoin` |
| LTC | 8 | 2.5 min | utxo | `litecoin` |
| GRIN | 9 | 1 min | mimblewimble | `grin` |
An unrecognized chain id resolves to a default profile shaped like XMR: 12 decimals, 120000 ms mean
block time, `monero` family, `monero` URI scheme. Callers therefore never crash on a mis-set
`CRYPTO_TYPE`. Adding a chain is one entry in the registry plus a family adapter.
Money math is exact: `src/money/atomic.js` operates on BigInt atomic units parameterized by
decimals, never floats.
## Game balance and pacing
A solo run is bounded by a random deadline drawn from an exponential distribution with mean equal to
the chain's block time, so the house edge is a convolution of the run-completion distribution with
block cadence rather than a number that can be set by hand. `src/sim/` drives the real engine with
headless bots and integrates the deadline out analytically, reporting a measured house win rate per
preset and per network.
`NETWORK_TUNING` in `src/game/difficultyConfig.js` scales dungeon depth to block time. Dungeon size
and monster behaviour come from the difficulty preset; the per-network entry overrides only the
level count:
| Chain | Levels |
|-------|--------|
| GRIN | 1 |
| XMR | 1 |
| LTC | 2 |
| WOW | 2 |
| BTC | 3 |
`NETWORK_TUNING_DISABLED=true` bypasses the override and leaves the preset's own level count in
place. Descending emits a `game_event`, which the client handles in
`html/js/network/socketHandlers.js` to surface level progress. See [BALANCE_SIM.md](BALANCE_SIM.md)
and [MULTI_LEVEL.md](MULTI_LEVEL.md).
## Client render kit
`html/js/render/` (exposed as `RK`) draws a shared `Scene` model in four techniques: Tiled, ASCII,
Iso and 3D. Art packs are interchangeable data, a pure-canvas FX layer supplies torch and hazard
effects, and the pack and technique available to a player come from the operator-owned cosmetic
catalog. Tiled and ASCII are free; Iso and 3D are premium. See [RENDER_PACKS.md](RENDER_PACKS.md).
## Provable fairness
`src/game/provablyFair.js` derives each dungeon from a committed seed, `src/game/fairnessVerifier.js`
exposes verification endpoints, and `src/views/verifyPage.js` renders the public verification page so
a player can reproduce a dungeon from its published proof.
## Conventions
- **Game id** is a UUID stored in `games.dungeon_seed`; `games.id` is a serial integer. Match game
objects with `WHERE dungeon_seed = $N`, never `WHERE id = $N`.
- **Socket ids are volatile** and change on refresh. Stable identity is the database `user_id` or the
session, never `socket_id`.
- **WOW has 11 decimals** (atomic divisor 1e11), unlike Monero's 12.
- **Cosmetic entitlements are per-pack**, resolved by tier and by lifetime credits purchased in
`src/multiplayer/entitlements.js`. Buying credits does not by itself unlock premium packs.
- **Migrations** live in `src/migrations/NNN_*.sql` and run in numeric order.
- **Tests**: `cd src && npm test` (Jest, `--runInBand`).
Source files carry JSDoc for API-level detail; these pages cover the seams between subsystems and
the operator-facing intent behind them.