实现账号绑定、持久登录与跨设备对局恢复
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
@@ -184,6 +184,16 @@ export class SessionRegistry {
|
||||
if (this.byTokenMap.get(s.token) === s) this.byTokenMap.delete(s.token);
|
||||
}
|
||||
|
||||
/** Rotate an authenticated session's bearer after attaching a new socket. @param {Session} s */
|
||||
rotateToken(s) {
|
||||
const previous = s.token;
|
||||
if (this.byTokenMap.get(previous) === s) this.byTokenMap.delete(previous);
|
||||
let token;
|
||||
do token = newToken(); while (token === previous || this.byTokenMap.has(token));
|
||||
s.token = token;
|
||||
this.byTokenMap.set(token, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* The reconnect window of one session: the registry's, or the session's own `resumeWindowMs` when that is longer
|
||||
* (the lobby may only extend it, never shorten it).
|
||||
@@ -526,6 +536,8 @@ export class Network {
|
||||
/** @type {Map<string, number>} open sockets per client network key */
|
||||
this.connsPerKey = new Map();
|
||||
this.closed = false;
|
||||
// Optional fork account policy; transport and the upstream game protocol remain usable when it is absent.
|
||||
this.accountPolicy = null;
|
||||
this.heartbeatTimer = setInterval(() => this.heartbeat(), this.opts.heartbeatMs);
|
||||
this.heartbeatTimer.unref?.();
|
||||
const sweepMs = Math.max(20, Math.min(15_000, Math.floor(this.opts.reconnectWindowMs / 4)));
|
||||
@@ -559,6 +571,7 @@ export class Network {
|
||||
if (this.closed) { try { ws.close(CLOSE.SHUTDOWN, 'server shutdown'); } catch { /* ignore */ } return; }
|
||||
const conn = new Connection(ws, clientAddress(req, this.opts.trustProxy), this.now(), this.opts);
|
||||
this.conns.set(ws, conn);
|
||||
if (this.accountPolicy) conn.account = this.accountPolicy.connection(req);
|
||||
if (conn.key) this.connsPerKey.set(conn.key, (this.connsPerKey.get(conn.key) || 0) + 1);
|
||||
ws.on('message', (data, isBinary) => {
|
||||
try { this.onFrame(conn, data, isBinary); } catch (e) { this.log.error('[net] frame handler crashed', e); }
|
||||
@@ -612,6 +625,9 @@ export class Network {
|
||||
}
|
||||
if (msg.t === 'hello') { this.onHelloMsg(conn, msg, now); return; }
|
||||
if (!conn.session) { this.reply(conn, errorMsg(ERR.BAD_MSG, rid, 'hello required')); return; }
|
||||
if (this.accountPolicy && !this.accountPolicy.authorized(conn)) {
|
||||
this.reply(conn, errorMsg(ERR.BAD_MSG, rid, 'account binding required')); return;
|
||||
}
|
||||
if (HEAVY_TYPES.has(msg.t) && !conn.heavy.take(now)) { this.reply(conn, errorMsg(ERR.RATE, rid, `${msg.t} too often`)); return; }
|
||||
|
||||
let res;
|
||||
@@ -640,6 +656,11 @@ export class Network {
|
||||
this.reply(conn, errorMsg(ERR.BAD_MSG, rid, `version mismatch: server ${PROTOCOL_VERSION}`));
|
||||
return;
|
||||
}
|
||||
if (this.accountPolicy) {
|
||||
const account = this.accountPolicy.hello(conn, msg);
|
||||
if (!account) { this.reply(conn, errorMsg(ERR.BAD_MSG, rid, 'account authentication required')); return; }
|
||||
msg = { ...msg, ...account };
|
||||
}
|
||||
const name = sanitizeName(msg.name);
|
||||
if (!name) { this.reply(conn, errorMsg(ERR.BAD_MSG, rid, 'bad field name')); return; }
|
||||
|
||||
@@ -670,6 +691,7 @@ export class Network {
|
||||
session.lastSeen = now;
|
||||
session.addr = conn.ip;
|
||||
session.limitKey = conn.key;
|
||||
this.accountPolicy?.attached?.(conn, { repeat });
|
||||
|
||||
let extra = null;
|
||||
try { extra = this.handler.welcomeInfo?.() ?? null; } catch (e) { this.log.error('[net] welcomeInfo crashed', e); }
|
||||
|
||||
Reference in new issue
Block a user