"use strict"; /* ================= 网络层:服务端 API 客户端 ================= */ const NET = { user: null, // {username,email} stats: null, // {bossKills,endlessHigh,runs} codexTotal: 62, online: false, async api(path, method, body) { let res; try { res = await fetch(path, { method: method || "GET", credentials: "same-origin", headers: body ? { "Content-Type": "application/json" } : undefined, body: body ? JSON.stringify(body) : undefined, }); } catch (e) { return { ok: false, status: 0, data: { error: "无法连接服务器" } }; } let data = {}; try { data = await res.json(); } catch (e) { /* 空响应 */ } return { ok: res.ok, status: res.status, data }; }, /* 启动:尝试恢复会话。true=已登录(未登录时服务端返回 200 {user:null}) */ async boot() { const r = await this.api("/api/me", "GET"); if (r.ok && r.data.user) { this._apply(r.data); return true; } return false; }, async login(id, password) { const r = await this.api("/api/login", "POST", { id, password }); if (r.ok) { this._apply(r.data); return null; } return r.data.error || "登陆失败"; }, async register(username, email, password) { const r = await this.api("/api/register", "POST", { username, email, password }); if (r.ok) { this._apply(r.data); return null; } return r.data.error || "注册失败"; }, async logout() { await this.api("/api/logout", "POST"); this.user = null; this.stats = null; this.online = false; SAVE.codex = {}; persist(); // 防同机下一账号继承图鉴 }, /* 应用服务端返回的用户数据:图鉴以服务端为准【替换】本地 (不能并集合并——localStorage 是浏览器级共享,同机多账号会互相污染,已踩坑) */ _apply(d) { this.user = d.user; this.stats = d.stats; this.codexTotal = d.codexTotal || 62; this.online = true; SAVE.codex = {}; if (Array.isArray(d.codex)) for (const k of d.codex) SAVE.codex[k] = true; persist(); }, /* 结算上报(fire-and-forget):成功后回写最新统计 */ submitRun(payload) { if (!this.user) return; this.api("/api/run", "POST", payload).then((r) => { if (r.ok) { this.stats = r.data.stats; if (Array.isArray(r.data.codex)) { for (const k of r.data.codex) SAVE.codex[k] = true; persist(); } } }).catch(() => { /* 网络失败静默:本地存档仍在 */ }); }, };