0.2.6-dev: the browser suites load the served manifests' content-hashed URLs (e2e servers wait for the asset index) and check Spine, audio and art through them; the history note of host bandwidth layers 0 and 1 for the next version; golden: unchanged
This commit is contained in:
1 parent
0bb16fd616
commit
409fec90d5
5 files changed
+155
-3
No files matched your search
@@ -464,3 +464,88 @@ continues the same layer gap (191 → 206). The human settle row first differs a
|
||||
267 → 282; final bonds move 奥术 27 → 30, 拉特兰 17 → 23 and 精准 53 → 59. This scenario was not among the 45 that
|
||||
already differed from 0.2.4 (§30.9, §30.16), so the release-relative total is now 46. The other 286 digests match
|
||||
the preceding candidate.
|
||||
|
||||
### 0.2.6-dev — host bandwidth, layers 0 and 1 (to be placed in the next version's file)
|
||||
|
||||
The maintainer's decision of 2026-10-12 on the bandwidth study (its measurements: a new player's first match costs the
|
||||
host about 52 MB, audio ≈ 40 % of it; returning players re-download after a re-extraction, a `setup --force`, a Docker
|
||||
rebuild or a changed tunnel address): do layer 0 (small fixes) and layer 1 (content-hashed URLs, long caching, brotli)
|
||||
first. Later layers (WebP / Opus variants, an asset-only service worker, a desktop client mode, importing an asset pack in
|
||||
the browser) are agreed and not part of this note. Nothing here touches the battle sim, the protocol messages or the
|
||||
committed data.
|
||||
|
||||
Layer 0:
|
||||
- **No BGM while the music is silent.** Muted, or the BGM volume at 0, `AudioManager.playBgm` only remembers the wanted
|
||||
track (a different one still playing stops); `setVolumes` fetches and starts it once the music is audible again. SFX
|
||||
and voices were already gated. Normative line: design/client.md Audio.
|
||||
- **WebSocket permessage-deflate** (`server/http/websocket.js WS_DEFLATE`): frames from 1 KB, no server context
|
||||
takeover, a 2 KB server window, zlib memLevel 7 (about 72 KB of zlib state per connection). [ASSUMED] the parameters
|
||||
come from one measured solo session (216 server frames: 169 KB → 63 KB; windows above 2 KB gained nothing); co-op
|
||||
messages are the same kinds. A client that offers a narrower server window than 2 KB, which ws would refuse with a 400,
|
||||
connects uncompressed instead (`narrowDeflateOffer`). Normative line: design/network.md §8 Transport.
|
||||
- **玩法说明 pages as WebP.** `tools/local-extract/extract.py` writes a WebP copy beside each of the 19 local pages and
|
||||
the manifest lists it (`GUIDE_WEBP_MODE`); the guide preloads only the next page instead of both neighbours. The
|
||||
copies are lossless (25.6 MB → 17.4 MB on the CN client's pages). Lossy quality 90 would make them 4.2 MB; per the
|
||||
decision that tutorial pages may go lossy only after a side-by-side comparison, that switch (one constant, then
|
||||
`extract.py --webp`) is left to the maintainer. Normative lines: design/client.md §13, ASSETS.md Post-processing.
|
||||
- **Traffic counters.** `GET /healthz?traffic=1` adds `traffic` (HTTP body bytes and requests by category — assets,
|
||||
audio, code, data, other — and 304s; WebSocket wire bytes after compression and payload bytes before it) and
|
||||
`assetHashes` (the index state). The 问题反馈 diagnostics gain a `traffic:` line: the page's Resource Timing
|
||||
`transferSize` by the same categories (`shared/assetPaths.js trafficCategory`), other sites apart. Nothing is stored
|
||||
or uploaded.
|
||||
- **DEPLOY.md**: a stable address keeps every player's cache, settings and reconnect token (a quick tunnel's new domain
|
||||
is a new site to the browser); HTTP/2 through the proxy saves round trips, not bytes; browsers offer brotli on HTTPS
|
||||
pages and localhost only; a Cloudflare cache rule for `/assets/`, `/fonts/`, `/media/` (with the risk note).
|
||||
|
||||
Layer 1:
|
||||
- **Content-hash index** (`server/http/assetIndex.js`): 1 s after the start, SHA-256 (16 hex) of every file under
|
||||
`public/assets/` and `public/fonts/` (dot names skipped, linked folders followed once), computed off the main thread,
|
||||
cached in `.cache/asset-hashes.json` by path + size + mtime (12,691 files / 724 MB here: 1.3 s the first time, 0.25 s
|
||||
after). A file whose size or mtime differs from its record is never vouched for; serving it asks for a rescan (at most
|
||||
one per 10 s). [ASSUMED] the 1 s delay and the 10 s rescan gap. A name that differs from a file's only in case finds
|
||||
that file (the manifest's `/assets/module/wah-y.png` is `WAH-Y.png` on this machine). `SP_ASSET_INDEX=0` turns it off.
|
||||
- **Served manifests** (`server/http/assetViews.js`): `/data/assets.json` and `/data/local-assets.json` go out with every
|
||||
art URL carrying its file's hash as the first segment (`/assets/~<hash>/…`, `/fonts/~<hash>/…`; audio stays
|
||||
`/assets/~<hash>/audio/….mp3`). A Spine model — a `{ skel, atlas, textures }` object of data/assets.json, a `spine/…`
|
||||
group of data/local-assets.json — carries one hash for all its files, since pixi-spine finds the atlas and the pages
|
||||
next to the skeleton. `no-cache` with the view's own ETag; rebuilt in memory when a manifest or the hashes change; the
|
||||
committed files are never written. Until the index is ready, without it, or on a static host: the files as committed.
|
||||
- **The `~hash` routes**: `static.js` strips the segment for `/assets/`, `/fonts/` and `/media/`; a hash that names the
|
||||
file's current bytes (its own or its model's) answers `public, max-age=31536000, immutable`, any other the current
|
||||
file with `no-cache` (an old page keeps working). Every indexed file's ETag is its content hash, on plain URLs too.
|
||||
Path segments, never `?v=`: `validSpine`'s `/\.skel$/`, `SKILL_MODE_FILE`'s `.mp3`, pixi-spine's basename and folder
|
||||
resolution all need the URL to end in the file name.
|
||||
- **Media**: `public/js/media.js` turns `/assets/~<hash>/audio/x.mp3` into `/media/~<hash>/x`; `server/http/media.js`
|
||||
resolves it like `/media/x`. The token death sound derived next to `charDie` drops `charDie`'s hash (`unhashedUrl`).
|
||||
- **Brotli** (`server/http/files.js`): quality 5 on the fly with the same memory cache (one entry per file and
|
||||
encoding), for clients that name `br` at least as high as gzip; gzip for the rest (`*` alone never picks brotli
|
||||
[ASSUMED]); `-br` / `-gz` ETag suffixes. Measured against gzip-6 on this repository: `.skel` −27 %, `data/*.json`
|
||||
−26 %, vendor −14 %, `public/js` and `server/sim` −5 %, `.atlas` −7 %.
|
||||
|
||||
Measured sample session (headless Chrome, a fresh profile, `test/e2e/fastServer.mjs` solo match from round 3 to the
|
||||
result after round 5, host bytes only, response headers included; the shop and the enemies vary between runs):
|
||||
|
||||
| session | code | data | assets | audio | WebSocket (server → client) | host total |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| before, music on | 3.16 MB | 0.67 MB | 9.0–9.7 MB | 7.9 MB | 163–179 KB (no compression) | 20.8–21.4 MB |
|
||||
| after, music on | 2.95 MB | 0.62 MB | 9.5 MB | 8.0 MB | 60 KB on the wire (173 KB payload) | 21.1 MB |
|
||||
| before, muted | 3.16 MB | 0.67 MB | 9.6 MB | 7.3 MB (BGM) | 165 KB | 20.7 MB |
|
||||
| after, muted | 2.95 MB | 0.62 MB | 9.5 MB | 0 | 57 KB on the wire | 13.1 MB |
|
||||
|
||||
A first visit with music on saves little (brotli on code and data, the WebSocket); a muted player saves the BGM. The
|
||||
caching gains are for returning players: within a day both versions answer from the browser cache, but after that the
|
||||
old version revalidated every asset of the session (≈ 340 requests here) and re-downloaded everything whose mtime had
|
||||
changed, while a hashed URL is never asked for again until its bytes change.
|
||||
|
||||
Open: `/fonts/fonts.css` (linked by index.html) and the woff2 files it names keep plain URLs (content ETags only); the
|
||||
optional versioned vendor URLs (layer 1.5) are not done; lossy tutorial pages wait for the comparison.
|
||||
|
||||
Tests: `test/ui/audio.test.js` (BGM gating; hashed audio through `/media/~hash/`), `test/ws-compression.test.js`,
|
||||
`test/local-extract.test.js` (guide WebP), `test/ui/localArt.test.js` (next-page preload), `test/traffic.test.js`,
|
||||
`test/asset-paths.test.js`, `test/diag.test.js` (traffic line), `test/asset-index.test.js` (index, views, `~hash`
|
||||
routes), `test/lobby.test.js` (brotli negotiation, streamed brotli, content ETags), `test/media-url.test.js`,
|
||||
`test/content/tokens_summons.test.js`; the browser suites `test/render/browser.test.js` (Spine through hashed URLs,
|
||||
`RENDER_E2E=1`), `test/ui/bond-audio.e2e.test.js` (`SP_E2E=1`, the cue through `/media/~hash/`) and
|
||||
`test/ui/real.e2e.test.js` (`SP_REAL_E2E=1`: the co-op run loads Spine, audio and art through hashed URLs). The e2e
|
||||
servers wait for the asset index before the first page (`test/e2e/client.mjs startRealServer`, the in-process ones in
|
||||
their `before`), so the browser suites see the URLs a player sees. Golden: unchanged.
|
||||
Reference in new issue
Block a user