The missed-moves banner stops counting games watched live
Once a gap opened — a backgrounded tab during the bots' turns was enough — the mark-seen condition (exactly one move behind) could never hold again, so the banner grew with every move a player watched happen in front of them. Now only the first state after arriving (or reconnecting) announces a gap; while present and visible, play marks itself seen, an announced gap stays frozen until watched or skipped, and a hidden tab still accumulates its gap honestly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4a4e164e88
commit
a60467c6fc
@@ -255,6 +255,9 @@ class Net {
|
|||||||
openingRolls = $state<{ rolls: Record<string, number[]>; first: string; players: string[] } | null>(null);
|
openingRolls = $state<{ rolls: Record<string, number[]>; first: string; players: string[] } | null>(null);
|
||||||
catchUp = $state<{ seq: number; actor: string; events: GameEvent[]; view: GameView; chat?: { player: string; text: string }[] }[] | null>(null);
|
catchUp = $state<{ seq: number; actor: string; events: GameEvent[]; view: GameView; chat?: { player: string; text: string }[] }[] | null>(null);
|
||||||
private seen: Record<string, number> = loadSeen();
|
private seen: Record<string, number> = loadSeen();
|
||||||
|
/** Room whose live stream this connection has already shown once: states
|
||||||
|
* after the first mark themselves seen while the tab is visible. */
|
||||||
|
private watching: string | null = null;
|
||||||
private currentSeq = 0;
|
private currentSeq = 0;
|
||||||
private lastYourTurn = new Map<string, boolean>();
|
private lastYourTurn = new Map<string, boolean>();
|
||||||
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
@@ -268,6 +271,7 @@ class Net {
|
|||||||
const ws = new WebSocket(SERVER_URL);
|
const ws = new WebSocket(SERVER_URL);
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
this.watching = null; // the first state after (re)connecting may carry a gap
|
||||||
this.status = "connected";
|
this.status = "connected";
|
||||||
// Mid-game reconnects (the socket dropped, not the page) walk straight
|
// Mid-game reconnects (the socket dropped, not the page) walk straight
|
||||||
// back to the table; otherwise the lobby ledger is the front door.
|
// back to the table; otherwise the lobby ledger is the front door.
|
||||||
@@ -317,13 +321,19 @@ class Net {
|
|||||||
this.view = msg.view;
|
this.view = msg.view;
|
||||||
if (typeof msg.seq === "number" && this.roomId) {
|
if (typeof msg.seq === "number" && this.roomId) {
|
||||||
this.currentSeq = msg.seq;
|
this.currentSeq = msg.seq;
|
||||||
const last = this.seen[this.roomId] ?? 0;
|
// Only the FIRST state after arriving carries a gap worth
|
||||||
this.missedMoves = Math.max(0, msg.seq - last);
|
// announcing. Later states were watched live: a caught-up
|
||||||
// Watching live counts as seeing; only a fresh arrival has a gap.
|
// watcher stays caught up, and an announced gap stays FROZEN
|
||||||
if (this.missedMoves === 0 || document.visibilityState === "visible") {
|
// (not grown, not wiped) until watched or skipped. A hidden
|
||||||
// A live update while present marks itself seen.
|
// tab accumulates its gap honestly.
|
||||||
if (last >= msg.seq - 1) this.markSeen();
|
if (this.watching === this.roomId && document.visibilityState === "visible") {
|
||||||
|
if (this.missedMoves === 0) this.markSeen();
|
||||||
|
} else {
|
||||||
|
const last = this.seen[this.roomId] ?? 0;
|
||||||
|
this.missedMoves = Math.max(0, msg.seq - last);
|
||||||
|
if (this.missedMoves === 0) this.markSeen();
|
||||||
}
|
}
|
||||||
|
if (document.visibilityState === "visible") this.watching = this.roomId;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user