game5 / 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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# lampion

A p2p night climb for up to 16 lanterns on a festival hill of rotating rings —
live at **[game5.ardegazu.ro](https://game5.ardegazu.ro)**, repo published at
**[git.ardegazu.ro/game5](https://git.ardegazu.ro/game5)**.

Monument-Valley geometry meets a summer-hills festival. Every player carries a
paper lantern from the campground at the base to the stage at the summit. The
hill is a stack of stone rings, and each level turns on its own — slow
clockwork with a fresh period and direction every round — so stair alignments
come and go by themselves. Stairs connect rings only where their slots align;
time your climb, or crank the ring you stand on to force an alignment early,
dragging every lantern on it along. Help or hinder. First lantern on the
summit stage takes the round;
when the last song ends with no summit, the highest lantern (earliest to get
there) takes it. First to 3 rounds headlines the night, and the sky fills with
fireworks and released lanterns.

Installable PWA (works saved to the iOS home screen), safe-area aware,
one-handed touch controls — built to be played standing in a field.

## Controls

- **← → / A D** (or ◀ ▶, or swipe) — walk around your ring (screen-relative:
  left on screen is left, even behind the hill)
- **↑ ↓ / W S** (or ▲ ▼, or swipe up/down) — climb / descend an aligned stair
  (the ▲ button glows when a stair lines up under you)
- **Q E / Z X** (or ⟲ ⟳) — rotate the ring you stand on, one slot per ~3 s
  cooldown; everyone standing on it rotates with you

## Architecture

- **No game server.** Browsers are [js-libp2p](https://libp2p.io) peers: they
  dial a circuit-relay-v2 peer on `signal.ardegazu.ro` over WSS, discover each
  other through gossipsub pubsub-peer-discovery, and upgrade to direct WebRTC
  (the circuit stays as signaling + a rate-limited fallback path). The net
  layer under [`client/src/game5/net/`](client/src/game5/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/game5/game/`](client/src/game5/game/).
- **Rooms without a room server.** Discovery is app-wide; membership is
  enforced by a room-scoped protocol id
  (`/lampion/2/<HKDF(secret) prefix>` — 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 ⇒ not a
  member. After that, libp2p's noise layer end-to-end encrypts every pair —
  even across the relay. The room secret lives in the URL fragment and never
  leaves the browser: the invite link **is** the room key.
- **One peer referees.** The first peer in an empty room claims host; on host
  loss the lowest surviving id takes over and voids the round. The host runs a
  fixed-rate simulation (125 ms ticks) and broadcasts compact deltas (steps,
  ring turns, a periodic full-position heartbeat); clients render from them
  with smooth interpolation. Rounds are seat-indexed so roster churn can't
  corrupt a live round; late joiners get a snapshot and spectate until the
  next song. The pure rules — local/world slot conversion, stair alignment,
  the tick, the referee — live in
  [`game/sim.cljs`](client/src/game5/game/sim.cljs), held to golden vectors.

## Run it yourself

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

```
cd client
npm install
npm run dev        # watch build → http://localhost:5173
npm run build      # static files in client/dist/
npm test           # pure game/net namespaces against the golden vectors
```

The dev server must run on port 4173 or 5173 — the shared relay's origin
allowlist covers those localhost origins plus `*.ardegazu.ro`. While the
shadow server runs, port 4173 serves the last release build from
`client/dist`. Open two tabs on the same `#room` URL to play against yourself.

## Publishing

`deploy/publish-repo.sh` builds `deploy/.site/`: this repo as a dumb-HTTP
clonable bare mirror (loose objects — IPFS-gateway friendly) plus the landing
page, and enforces the anonymity gate (single anonymous author, no identity
bytes in any object). The ecosystem root `git.ardegazu.ro` assembles and pins
all of these together.

MIT licensed. The authors are anonymous lanterns.

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