实现账号绑定、持久登录与跨设备对局恢复
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s
This commit is contained in:
1 parent
488db49f11
commit
7c25c89992
62 files changed
+6855
-1005
No files matched your search
@@ -73,6 +73,37 @@ Audio: autochess BGM per phase, UI SFX (buy/sell/refresh/level/merge/ready/timer
|
||||
|
||||
---
|
||||
|
||||
### Required account binding at the callsign field (fork integration)
|
||||
|
||||
The title hides 博士代号 until binding, then displays the role nickname as plain text without an input
|
||||
(`public/js/skland/entry.js`;
|
||||
`SP_SKLAND=1`, [skland.md](../skland.md)). The primary button shows 绑定账号 until a named role is fetched, then 开始.
|
||||
Multiple roles start with the default role and can be switched. Unbinding or an empty role list clears the callsign
|
||||
and restores 绑定账号. The nickname retains its person icon. The scan tab fetches a QR automatically and refreshes it
|
||||
every 10 s; the first awaiting-confirmation response starts a 60 s window. Refresh keeps the current image visible
|
||||
until its replacement is ready and updates the image source in place. Manual refresh remains available, with
|
||||
serial 2 s polling and paused timers outside the scan tab/visible page. Saved callsigns/entered flags and account recovery cannot skip the binding lookup. Skland QR
|
||||
login supports official/Bilibili channels; phone/password uses the Hypergryph account. Every game-entry HTTP request
|
||||
uses the existing game session token and a 30-day HttpOnly JWT cookie, without entering the lobby automatically.
|
||||
The cookie restores the role in a new tab or after a server restart; role choice persists on the server. The callsign
|
||||
never becomes an identity key: UID plus server region determine a stable game player id, allowing duplicate names.
|
||||
Account UI, styles and the entry policy stay in independent module files; title, boot and recovery have small hooks
|
||||
to reduce upstream merge conflicts. An optional server account policy also checks hello and room/game requests
|
||||
against the JWT-owned role; editing the client's UUID, region or name cannot select another player's binding.
|
||||
Unbind revokes remembered login. Owner request, 2026-10-14; §31.12 and [skland.md](../skland.md).
|
||||
|
||||
The title's 恢复账号对局 picker queries the selected role's live server-side seat, even on a device with no local match
|
||||
history. It replaces the visible 本机 recovery buttons (including the obsolete lobby shortcut). Opening it or restoring
|
||||
the login cookie does not disconnect an active device. 开始 or an explicit recovery confirmation asks for the role's
|
||||
current token, reconnects with the JWT, and enters only after welcome confirms the expected playerId. The existing
|
||||
socket is then replaced (4001, no auto-reconnect), and its old token cannot take the role back on reload. A duplicated
|
||||
tab or stale saved entered flag stays at the title unless its own current role token really resumed. Any anonymous
|
||||
welcome also clears an already-entered shell; the connection banner's 在此页面继续 uses checked account entry.
|
||||
The picker shows
|
||||
empty, loading and error states, a takeover notice and a refresh action; an expired/replaced selector cannot create a
|
||||
new match. Room/match state is still memory-only with the existing disconnect windows; server restart ends it.
|
||||
The UI and entry adapter remain in public/js/skland/ (§31.13).
|
||||
|
||||
## 13. Local-client art (optional, host-side)
|
||||
|
||||
`tools/local-extract/extract.py` (Python: UnityPy + lz4 + Pillow; decodes Arknights' custom **LZ4AK** bundle compression via `aklz4.py`) extracts the art the web download does not include (some of it, most official HUD sprites among it, is on the public mirror too — §22.5) from a locally installed client (CrossOver Windows build or PlayCover iOS build) into `public/assets/local/**` and writes `data/local-assets.json` (`{ groups: { '<subdir>': { name: { path, w, h, kind } } } }`). Everything here is **optional**: clients must render correctly when the file or any entry is missing (fall back to the web assets / procedural art). The emotes and the guide pages below are on the public mirror too (§22.5): fetch-assets downloads them (data/assets.json `ui['emoticon/<dir>/<picId>']`, `ui['guide/<key>']`) and the client shows the local copy first, then that one (`data.js artUrls`); docs/DEPLOY.md §6 lists what still needs the client.
|
||||
|
||||
@@ -4,6 +4,26 @@ Part of [DESIGN.md](../DESIGN.md) (the index; section numbers are global).
|
||||
|
||||
## 8. Network protocol (shared/protocol.js is normative)
|
||||
|
||||
**Fork account policy (opt-in `SP_SKLAND=1`, owner request 2026-10-14).** The existing WebSocket messages are
|
||||
unchanged. Hello may establish anonymous transport for account login, but room/game requests require the role
|
||||
owned by a persistent, server-verified JWT cookie. The server ignores a client's claimed nickname/UUID for account
|
||||
identity; it derives stable `playerId` from UID plus provider-reported server region (`channelName`). Nicknames
|
||||
may repeat, and optional provider `gameId` metadata does not affect identity. The public id uses 128 SHA-256 digest
|
||||
bits encoded as base64url after `a_` (24 characters), so normal field ids and per-player battle reports fit the
|
||||
existing protocol limits. A reconnect token alone cannot resume
|
||||
an account identity. A valid login plus the role's current reconnect token can resume its previous socket; each
|
||||
game frame checks the current login record and selected role, so logout or a role switch invalidates its authority.
|
||||
Cookie restoration and read-only recovery lookup never take over another device. An explicit authenticated
|
||||
enter/resume POST returns the selected role's token; the existing hello then attaches it and resends room/match state.
|
||||
Only after successful attachment is the account token rotated, so a stale device cannot silently retake the seat.
|
||||
The client stages the entry token in memory until welcome; local multi-tab claims still protect normal reconnects.
|
||||
Login lasts 30 days, survives process restart and is revoked on unbind. Room/match data remain in memory.
|
||||
Recovery selectors are scoped to the selected role and the particular room/match; stale selectors return 409 without
|
||||
creating a new game. The existing 10-minute / solo 24-hour disconnect windows are unchanged, and an expired seat is
|
||||
removed through the lobby before its stable playerId can be reused. Account API ownership and private DB handling
|
||||
are specified in [skland.md](../skland.md) §5/§9. The optional policy hooks leave module-off transport unchanged;
|
||||
no simulation rule, WebSocket message or protocol-version change is involved.
|
||||
|
||||
Transport: one WebSocket per tab at `/ws`, JSON text frames `{ t: '<type>', ...payload }`. Client requests may carry `rid` (request id); the server's direct reply echoes `rid` (`ok` or `error`). Server pushes have no `rid`. Max frame 64 KB inbound (a compressed frame counts its inflated size); rate limit 40 msgs/s per connection (excess dropped with `error RATE`). Frames from 1 KB up are compressed with permessage-deflate (RFC 7692; `server/http/websocket.js WS_DEFLATE`: no server context takeover, a 2 KB server window, zlib memLevel 7 — about 72 KB of zlib state per connection); smaller frames go out plain, and a client that offers no compression, or caps the server window below 2 KB, gets plain frames. A measured solo session (rounds 3–5, `/healthz?traffic=1`): 173 KB of server frames → 60 KB on the wire.
|
||||
|
||||
### 8.1 Session & lobby
|
||||
@@ -137,6 +157,8 @@ voting, refuses stale ids and cancels on disconnect/leave; DESIGN §28.21 / META
|
||||
|
||||
Explicit local recovery (§28.20) reuses saved hello tokens after a fresh live-tab claim check. Display names and room
|
||||
codes are UI metadata only, never authentication. No new network message or nickname/IP matching is introduced.
|
||||
The account-required fork replaces the visible local recovery entry with authenticated account recovery (§31.13);
|
||||
the generic local helper and its module-off handshake behavior are retained.
|
||||
|
||||
Optional `coins: [[id, balance, cap]]` is an integer skill-currency readout (§29.6), separate from ammo. It includes zero, is stepped between snapshots and clears when omitted. Result-validation names for board operators are resolved from the server's DIY/stand-in selections, never a supplied display name.
|
||||
|
||||
|
||||
Reference in new issue
Block a user