修复同机多账号数据串号(排行榜第二名复制第一名数据):①图鉴同步由并集合并改为服务端权威替换(localStorage浏览器级共享,并集会把上任账号图鉴推给下任);②结算不再上报localStorage历史最高分(endless_high只认本账号实际上报的无尽对局);③登出清空本地图鉴。已备份并清洗'你好'的污染数据(17条继承图鉴+195081假最高分,其真实成绩=击坠2)。playwright双账号同机回归:B账号0继承、榜单四行各异

This commit is contained in:
JOJO 2026-08-06 10:45:49 +08:00
parent 2cea368db6
commit 0d75304335
3 changed files with 9 additions and 10 deletions

View File

@ -45,19 +45,19 @@ const NET = {
async logout() { async logout() {
await this.api("/api/logout", "POST"); await this.api("/api/logout", "POST");
this.user = null; this.stats = null; this.online = false; this.user = null; this.stats = null; this.online = false;
SAVE.codex = {}; persist(); // 防同机下一账号继承图鉴
}, },
/* 应用服务端返回的用户数据:同步图鉴解锁到本地存档(并集) */ /*
不能并集合并localStorage 是浏览器级共享同机多账号会互相污染已踩坑 */
_apply(d) { _apply(d) {
this.user = d.user; this.user = d.user;
this.stats = d.stats; this.stats = d.stats;
this.codexTotal = d.codexTotal || 62; this.codexTotal = d.codexTotal || 62;
this.online = true; this.online = true;
if (Array.isArray(d.codex)) { SAVE.codex = {};
SAVE.codex = SAVE.codex || {}; if (Array.isArray(d.codex)) for (const k of d.codex) SAVE.codex[k] = true;
for (const k of d.codex) SAVE.codex[k] = true; persist();
persist();
}
}, },
/* 结算上报fire-and-forget成功后回写最新统计 */ /* 结算上报fire-and-forget成功后回写最新统计 */

View File

@ -242,8 +242,7 @@ function submitRun(win){
if(typeof NET==="undefined"||!NET.user)return; if(typeof NET==="undefined"||!NET.user)return;
NET.submitRun({mode:G.mode,diff:G.diff,score:G.score,wave:G.wave,stage:G.stage, NET.submitRun({mode:G.mode,diff:G.diff,score:G.score,wave:G.wave,stage:G.stage,
timeSec:Math.round(G.time/60),bossKills:G.bossKills||0,win:!!win, timeSec:Math.round(G.time/60),bossKills:G.bossKills||0,win:!!win,
bestEndless:Math.max(SAVE.best.e0||0,SAVE.best.e1||0), codex:Object.keys(SAVE.codex)}); // 注意:不上报 localStorage 历史最高分SAVE.best 是浏览器级共享,会串号)
codex:Object.keys(SAVE.codex)});
} }
function endRun(win){ function endRun(win){
if(UI==="over")return; if(UI==="over")return;

View File

@ -113,11 +113,11 @@ function run(req, res, ctx, body) {
if (!vInt(b.wave, 0, 9999) || !vInt(b.stage, 0, 99)) return ctx.fail(400, "非法进度"); if (!vInt(b.wave, 0, 9999) || !vInt(b.stage, 0, 99)) return ctx.fail(400, "非法进度");
if (!vInt(b.timeSec, 0, 86400)) return ctx.fail(400, "非法时长"); if (!vInt(b.timeSec, 0, 86400)) return ctx.fail(400, "非法时长");
if (!vInt(b.bossKills, 0, 60)) return ctx.fail(400, "非法击坠数"); if (!vInt(b.bossKills, 0, 60)) return ctx.fail(400, "非法击坠数");
if (!vInt(b.bestEndless === undefined ? 0 : b.bestEndless, 0, 1e8)) return ctx.fail(400, "非法纪录");
const keys = Array.isArray(b.codex) ? b.codex.slice(0, 100).filter((k) => CODEX_KEYS.has(k)) : []; const keys = Array.isArray(b.codex) ? b.codex.slice(0, 100).filter((k) => CODEX_KEYS.has(k)) : [];
const uid = ctx.user.id, now = Date.now(); const uid = ctx.user.id, now = Date.now();
const endlessCand = Math.max(b.mode === "endless" ? b.score : 0, b.bestEndless || 0); /* endless_high 只认本账号实际上报的无尽对局分数(不收 localStorage 历史值——浏览器级共享会串号,已踩坑) */
const endlessCand = b.mode === "endless" ? b.score : 0;
db.exec("BEGIN"); db.exec("BEGIN");
try { try {
qInsRun.run(uid, b.mode, b.diff, b.score, b.wave, b.stage, b.timeSec, b.bossKills, b.win ? 1 : 0, now); qInsRun.run(uid, b.mode, b.diff, b.score, b.wave, b.stage, b.timeSec, b.bossKills, b.win ? 1 : 0, now);