| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b594066 commit 2c2f6f5
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -370,3 +370,17 @@ this repo is its proof. | |||
| 370 | 370 | and no guidance; a design note in the plugin docs would spare every | |
| 371 | 371 | author the same deliberation. Eleven plugins now persist in | |
| 372 | 372 | `pluginDir` — the most settled seam in the api. | |
| 373 | + | ||
| 374 | + - **Cross-engine float determinism** (globs/): `Math.hypot`, `Math.pow`, | ||
| 375 | + and library trig are implementation-defined precision; node 24 and | ||
| 376 | + headless Chromium disagreed on 4/20 knife-edge bot-mirror matches when a | ||
| 377 | + game sim was replayed on both. Fix in the sim itself: `sqrt(x*x+y*y)`, | ||
| 378 | + a pinned pow literal, and range-reduced Taylor trig — then 40/40 matches | ||
| 379 | + bit-identical. Any plugin acting as physics authority for a browser | ||
| 380 | + client will hit this; not an api seam, but worth knowing before trusting | ||
| 381 | + "same JS everywhere". | ||
| 382 | + - **In-band WS bearer** (globs/): browsers can't set upgrade headers, so | ||
| 383 | + the plugin lifts `hello{token}` to an agent via `getAgent` with a | ||
| 384 | + synthetic headers-only request — valid for bearers per the auth.js | ||
| 385 | + contract, not for DPoP-bound tokens. Third WS plugin to want this; | ||
| 386 | + a documented `api.auth.getAgentFromToken(token)` would make it official. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -95,6 +95,7 @@ README and NOTES.md, ship the closest honest approximation. | |||
| 95 | 95 | | `forge/` | #322-next | git forge — hosting, GitHub-light UI + JSON API, forks/PRs with real merges, issues as pod resources, did:nostr + xlogin, Bitcoin-anchored history (Blocktrails); zero deps, zero build | | |
| 96 | 96 | | `recordweb/` | — | [RecordWeb](https://github.com/recordweb) (RWP) node — institutional Records with `did:rwp`, content-addressed immutable snapshots in a version DAG (RFC 8785 + SHA-256 + Ed25519), one-way finalization, Merkle-rooted Cases; DID resolver + verify; **zero deps, zero seam gaps** — the four landed seams suffice for a from-scratch protocol | | |
| 97 | 97 | | `ripple/` | [webcontracts#4](https://github.com/webcontracts/webcontracts.github.io/issues/4) | Ryan Fugger's ORIGINAL Ripple (2004) — bilateral trustlines, BFS payment routing through chains of pre-granted credit, creditor-only settle, hash-chained transition log (trustline.v1); signed single-balance bookkeeping, bigint micro-units, UI page; single-server atomicity honest, federation is the named next flavour | | |
| 98 | + | `globs/` | — | ranked realtime match server for [NEONGLOBS](https://melvincarvalho.github.io/neonglobs/) (a Globulos tribute) — matchmaking + bots + ELO + leaderboard over `ws.route`; server re-runs the game's deterministic sim as authority, vendored with a **bit-identical node↔Chromium parity contract** (the cross-engine float-determinism finding) | | ||
| 98 | 99 | ||
| 99 | 100 | Plus one directory that is a scenario, not a plugin: | |
| 100 | 101 | [`federation-demo/`](./federation-demo/) — **two** JSS instances federating | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,6 +62,7 @@ describe('composition: every plugin on one server', () => { | |||
| 62 | 62 | plugins: [ | |
| 63 | 63 | { module: at('relay/plugin.js'), prefix: '/relay' }, | |
| 64 | 64 | { module: at('webrtc/plugin.js'), prefix: '/webrtc' }, | |
| 65 | + { module: at('globs/plugin.js'), prefix: '/globs', config: { aimTimeMs: 300 } }, | ||
| 65 | 66 | { module: at('terminal/plugin.js'), prefix: '/terminal', config: { token: 'compose-secret' } }, | |
| 66 | 67 | { module: at('tunnel/plugin.js'), prefix: '/tunnel' }, | |
| 67 | 68 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,67 @@ | |||
| 1 | + # globs — NEONGLOBS realtime match server | ||
| 2 | + | ||
| 3 | + Ranked online play for [NEONGLOBS](https://melvincarvalho.github.io/neonglobs/) | ||
| 4 | + (a Globulos tribute): matchmaking, server-authoritative physics, three bot | ||
| 5 | + levels, ELO ratings, and a leaderboard — as a #206 loader plugin. | ||
| 6 | + | ||
| 7 | + ```js | ||
| 8 | + plugins: [{ id: 'globs', module: 'globs/plugin.js', prefix: '/globs' }] | ||
| 9 | + ``` | ||
| 10 | + | ||
| 11 | + - `WS {prefix}/play` — the match protocol (documented at the top of | ||
| 12 | + `plugin.js`): `hello` → `queue` → `matched` → per round | ||
| 13 | + `phase`/`commit`/`reveal`/`result` → `matchEnd`. | ||
| 14 | + - `GET {prefix}/leaderboard` — top 50 by rating, player count, bot anchors. | ||
| 15 | + | ||
| 16 | + ## Why this game networks so well | ||
| 17 | + | ||
| 18 | + NEONGLOBS rounds are *simultaneous secret commitments* resolved by a | ||
| 19 | + deterministic, fixed-timestep sim — a round is a pure function of its | ||
| 20 | + commitments. So the server relays one small message per round, re-runs the | ||
| 21 | + same sim as the single authority, and never streams a tick. `./sim.js` is | ||
| 22 | + vendored from the game repo, where `tools/parity.sh` proves node and | ||
| 23 | + headless Chromium produce **bit-identical** match results for the same | ||
| 24 | + seeds (that took work — see findings). | ||
| 25 | + | ||
| 26 | + ## Identity and ranking | ||
| 27 | + | ||
| 28 | + `api.auth.getAgent` on the WS upgrade (node clients send `Authorization`), | ||
| 29 | + or `hello{token}` for browsers — the token is lifted to an agent via | ||
| 30 | + `getAgent` with a synthetic headers-only request, which the `auth.js` | ||
| 31 | + contract documents as sufficient for bearer verification. Guests play | ||
| 32 | + unrated. ELO: start 1200, K=32 vs humans, K=16 vs bots; bots are fixed | ||
| 33 | + anchors (EASY 800 / MEDIUM 1100 / HARD 1400) that never move. Both deltas | ||
| 34 | + are computed from pre-match ratings before either is applied. Disconnect | ||
| 35 | + mid-match forfeits. State: `elo.json` (atomic tmp+rename) and | ||
| 36 | + `matches.jsonl` in `pluginDir`. | ||
| 37 | + | ||
| 38 | + ## Findings | ||
| 39 | + | ||
| 40 | + - **`ws.route` + `getAgent` + `pluginDir` covered the whole service** — a | ||
| 41 | + ranked realtime game server needed nothing beyond the documented surface. | ||
| 42 | + Zero new seams. | ||
| 43 | + - **Browser WS auth is the same gap every WS plugin has**: browsers cannot | ||
| 44 | + set upgrade headers, so the plugin lifts a bearer sent in-band | ||
| 45 | + (`hello{token}`) via `getAgent({ headers: { authorization } , ... })`. | ||
| 46 | + Works because bearer verification only reads headers; a DPoP-bound token | ||
| 47 | + would not survive this path (documented limitation, same as core's | ||
| 48 | + `.webrtc`). | ||
| 49 | + - **Cross-engine float determinism is real and it bites.** With stock | ||
| 50 | + `Math.hypot`/`Math.sin`/`Math.cos`/`Math.atan2`/`Math.pow`, node 24 and | ||
| 51 | + Chromium disagreed on 4 of 20 solver-mirror matches (implementation- | ||
| 52 | + defined precision; knife-edge games amplify ulps). The game and this | ||
| 53 | + vendored sim now use `sqrt(x²+y²)`, a pinned FRICTION literal, and | ||
| 54 | + range-reduced Taylor-series trig — after which 40/40 matches are | ||
| 55 | + bit-identical across engines. Any plugin that replays client physics | ||
| 56 | + should expect this. | ||
| 57 | + - **The server pushes `welcome` immediately after upgrade**, which beats a | ||
| 58 | + message listener attached after the client's `open` event — the test | ||
| 59 | + buffers from socket creation. Same race webrtc/ fixed on the server side | ||
| 60 | + for the opposite direction. | ||
| 61 | + | ||
| 62 | + ## What maps / what doesn't | ||
| 63 | + | ||
| 64 | + Maps: the full ranked-match loop, bots, leaderboard, forfeits, keepalive | ||
| 65 | + pings. Doesn't: spectators, reconnection grace (disconnect = forfeit), | ||
| 66 | + multiple tables (soccer only, like the game), and rating decay — all | ||
| 67 | + protocol-compatible extensions, none blocked by the api. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments