game4 / README.md
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# séance

A p2p haunted-house party game for 1–16 phones and **three real rooms of a
house** — live at **[game4.ardegazu.ro](https://game4.ardegazu.ro)**, repo
published at **[git.ardegazu.ro/game4](https://git.ardegazu.ro/game4)**.

Every time the bell tolls, everyone physically walks to one of three rooms
(the Study, the Parlour, the Cellar) and taps it on their phone. A ghost
sweeps one room — it favours wherever the crowd stood last bell. Everyone
caught loses a candle; three candles and you're out; the last lit candle wins
the séance. Each bell one player is secretly the **medium** and learns a room
the spirit will shun — they can share it out loud, stay silent, or lie.
Installable PWA (works saved to the iOS home screen), safe-area aware.

## Architecture

- **No game server.** Browsers are [js-libp2p](https://libp2p.io) peers: they
  dial a shared circuit-relay-v2 peer on `signal.ardegazu.ro` over WSS, find
  each other through gossipsub pubsub-peer-discovery, and upgrade to direct
  WebRTC (the circuit doubles as signaling and a rate-limited fallback path).
  The p2p layer under [`client/src/game4/net/`](client/src/game4/net/) is
  vendored from [neon-grid](https://git.ardegazu.ro/game1/) and is
  game-agnostic except for the protocol id — the game only adds its own wire
  protocol and logic in [`client/src/game4/game/`](client/src/game4/game/).
- **Rooms are cryptographic.** Discovery is app-wide; membership in a room is
  enforced by a room-scoped protocol id (`/seance/2/<HKDF(secret)>` — wrong
  rooms fail protocol negotiation) plus a mutual sealed hello: one AES-256-GCM
  envelope keyed from the room secret, bound to both noise-authenticated peer
  ids. Undecryptable means not a member. After that, frames ride libp2p's
  noise encryption, end-to-end per pair even across the relay.
- **One peer conducts.** The lowest peer id runs the authoritative bell timers
  and the ghost ([`game/game.cljs`](client/src/game4/game/game.cljs)): it collects secret
  room picks, resolves each bell, and broadcasts compact phase frames. Séances
  are seat-indexed so roster churn can't corrupt one in flight. Host loss voids
  the séance and the next-lowest id takes over. Late joiners get a snapshot and
  spectate until the next séance. The 16-guest cap is enforced by the conductor
  (the old relay's room cap no longer exists on this stack).
- **Where you stand is the honor system.** The game can't see your feet — your
  tap is your room. Walking is the point, not the enforcement.
- **Hosting without an origin server.** `client/dist/` is pinned to IPFS,
  named by IPNS and served straight from the gateway on the app's own domain
  (DNSLink). The published repo works the same way (`deploy/publish-repo.sh`
  + the shared `git.ardegazu.ro` source root).

## Develop

Needs node ≥ 22, a JVM ≥ 17 and the `clojure` CLI (shadow-cljs).

```bash
cd client && npm install && npm run dev   # watch build → http://localhost:5173
```

Open two tabs on the same `#room` URL to play against yourself
(keys 1/2/3 pick rooms on desktop). While the shadow server runs, port 4173
serves the last release build from `client/dist`. `npm test` compiles the pure
game/net namespaces and runs the golden vectors (extracted from the TypeScript
implementation while it was canon). Regenerate icons with `npm run icons`.

## Deploy

```bash
cd client && npm run build     # → client/dist
# then: ird ipfs add client/dist → ird ipfs ipns publish game4.ardegazu.ro <cid>

deploy/publish-repo.sh         # → deploy/.site (landing page + clonable bare mirror)
# then: ../git/assemble.sh game4   (updates git.ardegazu.ro)
```

MIT — see [LICENSE](LICENSE).

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/game4.git