A refused seat is logged, and the wallet drops a seat only on the second refusal in a row
A lost seat mid-game is the costliest quiet failure this table has: one refusal around a restart cost Kestrel PRPM's seat with nothing in the journal to say why. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
f738957100
commit
1ebd6dbe47
@@ -717,7 +717,12 @@ wss.on("connection", (socket, req) => {
|
|||||||
const room = getRoom(roomId);
|
const room = getRoom(roomId);
|
||||||
if (!room) return send(socket, { type: "error", message: "no such room" });
|
if (!room) return send(socket, { type: "error", message: "no such room" });
|
||||||
const result = joinRoom(room, name, typeof msg.token === "string" ? msg.token : null);
|
const result = joinRoom(room, name, typeof msg.token === "string" ? msg.token : null);
|
||||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
if ("error" in result) {
|
||||||
|
// A refused seat is worth a line: a lost seat mid-game is the
|
||||||
|
// costliest quiet failure this table has.
|
||||||
|
console.warn(`join refused: room ${room.id} name ${JSON.stringify(name)} — ${result.error}`);
|
||||||
|
return send(socket, { type: "error", message: result.error });
|
||||||
|
}
|
||||||
leaveGallery(session);
|
leaveGallery(session);
|
||||||
session.playerId = name;
|
session.playerId = name;
|
||||||
session.roomId = room.id;
|
session.roomId = room.id;
|
||||||
@@ -1065,6 +1070,7 @@ wss.on("connection", (socket, req) => {
|
|||||||
const result = peekSummary(roomId, name, typeof seat.token === "string" ? seat.token : null);
|
const result = peekSummary(roomId, name, typeof seat.token === "string" ? seat.token : null);
|
||||||
if (result === null) continue;
|
if (result === null) continue;
|
||||||
if (result === "badToken") {
|
if (result === "badToken") {
|
||||||
|
console.warn(`seat voided: room ${roomId} name ${JSON.stringify(name)} — the token did not match`);
|
||||||
voided.push(`${roomId}:${name}`);
|
voided.push(`${roomId}:${name}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -380,6 +380,8 @@ class Net {
|
|||||||
feedbackReports = $state<FeedbackReportView[]>([]);
|
feedbackReports = $state<FeedbackReportView[]>([]);
|
||||||
/** Every seat this browser holds, across rooms. */
|
/** Every seat this browser holds, across rooms. */
|
||||||
seats = $state<Seat[]>(loadSeats());
|
seats = $state<Seat[]>(loadSeats());
|
||||||
|
/** Seats the last games answer refused: dropped only if refused again. */
|
||||||
|
private voidedOnce = new Set<string>();
|
||||||
/** Lobby ledger: one summary per live seat. */
|
/** Lobby ledger: one summary per live seat. */
|
||||||
stats = $state<Record<string, number | string | null> | null>(null);
|
stats = $state<Record<string, number | string | null> | null>(null);
|
||||||
chatSeen = $state<Record<string, number>>(loadChatSeen());
|
chatSeen = $state<Record<string, number>>(loadChatSeen());
|
||||||
@@ -673,8 +675,13 @@ class Net {
|
|||||||
// stays — a restarting server, a stale restore, or the wrong
|
// stays — a restarting server, a stale restore, or the wrong
|
||||||
// backend all answer with ignorance, and ignorance is not
|
// backend all answer with ignorance, and ignorance is not
|
||||||
// deletion. The wallet is capped by age instead of by trust.
|
// deletion. The wallet is capped by age instead of by trust.
|
||||||
|
// And a seat is dropped only on the SECOND refusal in a row: one
|
||||||
|
// refusal around a restart or a stale answer must not cost a seat.
|
||||||
const voided = new Set((msg.voided as string[] | undefined) ?? []);
|
const voided = new Set((msg.voided as string[] | undefined) ?? []);
|
||||||
let kept = this.seats.filter((s) => !voided.has(`${s.roomId}:${s.name}`));
|
const doubted = new Set<string>();
|
||||||
|
for (const key of voided) { if (this.voidedOnce.has(key)) doubted.add(key); }
|
||||||
|
this.voidedOnce = voided;
|
||||||
|
let kept = this.seats.filter((s) => !doubted.has(`${s.roomId}:${s.name}`));
|
||||||
if (kept.length > 50) kept = kept.slice(kept.length - 50);
|
if (kept.length > 50) kept = kept.slice(kept.length - 50);
|
||||||
if (kept.length !== this.seats.length) {
|
if (kept.length !== this.seats.length) {
|
||||||
this.seats = kept;
|
this.seats = kept;
|
||||||
|
|||||||
Reference in New Issue
Block a user