Table talk keeps its place in the chronicle
Live chat interleaved correctly, but a reload sank every past message to the bottom: the rejoin sent the whole game history first and the chat history after. Talk is now a tableTalk event in the room's event stream at the moment it was said — the room file already preserved the interleaving, since chat lines append between command lines — so rejoining players read the banter between the battle lines where it happened. The separate chatHistory message retires; unread counting rides the event stream instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ef661c79ad
commit
5802e1df05
@@ -330,3 +330,48 @@
|
|||||||
[ 1383416ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
[ 1383416ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
[ 1388071ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
[ 1388071ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
[ 1391775ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
[ 1391775ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1395068ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1401362ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1405927ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1409973ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1415788ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1419993ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1422568ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1427897ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1431247ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1436017ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1440676ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1444279ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1447985ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1453812ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1459007ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1464539ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1469651ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1472486ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1478828ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1483427ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1488790ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1492253ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1496471ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1500655ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1504766ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1507334ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1513726ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1517718ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1520603ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1523915ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1529894ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1535783ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1539666ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1543362ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1546661ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1550202ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1553917ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1559517ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1562654ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1566220ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1570798ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1573997ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1577376ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1583494ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
[ 1588863ms] [ERROR] WebSocket connection to 'ws://127.0.0.1:8899/ws' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://127.0.0.1:8899/assets/index-C9p8jmUs.js:1
|
||||||
|
|||||||
@@ -501,6 +501,7 @@ export type GameEvent =
|
|||||||
| { type: "moveBumped"; player: PlayerId; direction: Side }
|
| { type: "moveBumped"; player: PlayerId; direction: Side }
|
||||||
| { type: "attackMisdirected"; attacker: PlayerId; intended: PlayerId; rolledDirection: Side; newTarget: PlayerId | null }
|
| { type: "attackMisdirected"; attacker: PlayerId; intended: PlayerId; rolledDirection: Side; newTarget: PlayerId | null }
|
||||||
| { type: "dieRolled"; player: PlayerId | null; roll: number; purpose: string }
|
| { type: "dieRolled"; player: PlayerId | null; roll: number; purpose: string }
|
||||||
|
| { type: "tableTalk"; player: PlayerId; text: string }
|
||||||
| { type: "retreatedInHorror"; player: PlayerId; from: Cell; to: Cell }
|
| { type: "retreatedInHorror"; player: PlayerId; from: Cell; to: Cell }
|
||||||
| { type: "illusionWallCreated"; caster: PlayerId; edge: { cell: Cell; side: Side } }
|
| { type: "illusionWallCreated"; caster: PlayerId; edge: { cell: Cell; side: Side } }
|
||||||
| { type: "illusionTested"; player: PlayerId; edge: string; result: "believes" | "seesThrough" }
|
| { type: "illusionTested"; player: PlayerId; edge: string; result: "believes" | "seesThrough" }
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
// {type:"events", events} redacted for this recipient
|
// {type:"events", events} redacted for this recipient
|
||||||
// {type:"state", view, seq} redacted full view (after every change)
|
// {type:"state", view, seq} redacted full view (after every change)
|
||||||
// {type:"chat", player, text, at} one line of table talk
|
// {type:"chat", player, text, at} one line of table talk
|
||||||
// {type:"chatHistory", messages} the room's talk so far, on join
|
|
||||||
// {type:"transferCode"|"transferClaimed"|"catchUp"|"games"|"stats"}
|
// {type:"transferCode"|"transferClaimed"|"catchUp"|"games"|"stats"}
|
||||||
// {type:"error", message}
|
// {type:"error", message}
|
||||||
|
|
||||||
@@ -250,9 +249,6 @@ wss.on("connection", (socket) => {
|
|||||||
if (room.state) {
|
if (room.state) {
|
||||||
send(socket, { type: "events", events: redactFor(room.events, name) });
|
send(socket, { type: "events", events: redactFor(room.events, name) });
|
||||||
}
|
}
|
||||||
if (room.chat.length > 0) {
|
|
||||||
send(socket, { type: "chatHistory", messages: room.chat.slice(-100) });
|
|
||||||
}
|
|
||||||
broadcastRoomState(room);
|
broadcastRoomState(room);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,6 +223,9 @@ export function addChat(room: Room, playerId: PlayerId, rawText: string): { text
|
|||||||
const at = new Date().toISOString();
|
const at = new Date().toISOString();
|
||||||
room.chat.push({ player: playerId, text, at });
|
room.chat.push({ player: playerId, text, at });
|
||||||
if (room.chat.length > CHAT_KEEP) room.chat.splice(0, room.chat.length - CHAT_KEEP);
|
if (room.chat.length > CHAT_KEEP) room.chat.splice(0, room.chat.length - CHAT_KEEP);
|
||||||
|
// Talk takes its place in the chronicle where it was said, so rejoining
|
||||||
|
// players see it interwoven with the action, not sunk to the bottom.
|
||||||
|
room.events.push({ type: "tableTalk", player: playerId, text });
|
||||||
appendLine(room.id, { kind: "chat", player: playerId, text, at });
|
appendLine(room.id, { kind: "chat", player: playerId, text, at });
|
||||||
return { text, at };
|
return { text, at };
|
||||||
}
|
}
|
||||||
@@ -435,7 +438,9 @@ export function loadPersistedRooms(): void {
|
|||||||
const r = startInMemory(room, line.expansion, line.colors, line.deckRev);
|
const r = startInMemory(room, line.expansion, line.colors, line.deckRev);
|
||||||
if ("error" in r) throw new Error(`replay start failed: ${r.error}`);
|
if ("error" in r) throw new Error(`replay start failed: ${r.error}`);
|
||||||
} else if (line.kind === "chat") {
|
} else if (line.kind === "chat") {
|
||||||
|
// File order preserves the interleaving with commands.
|
||||||
room.chat.push({ player: line.player, text: line.text, at: line.at });
|
room.chat.push({ player: line.player, text: line.text, at: line.at });
|
||||||
|
room.events.push({ type: "tableTalk", player: line.player, text: line.text });
|
||||||
} else if (line.kind === "command") {
|
} else if (line.kind === "command") {
|
||||||
if (!room.state) throw new Error("command before start in log");
|
if (!room.state) throw new Error("command before start in log");
|
||||||
const result = applyCommand(room.state, line.playerId, line.command as Command);
|
const result = applyCommand(room.state, line.playerId, line.command as Command);
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ export function humanize(e: GameEvent): string | null {
|
|||||||
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name} — actions over.`;
|
case "objectPickedUp": return `${e.player} picks up the ${cardDef(e.card.cardId).name} — actions over.`;
|
||||||
case "wardSet": return e.armed ? "Your ward is set — the next thief bleeds." : "Your ward stands down.";
|
case "wardSet": return e.armed ? "Your ward is set — the next thief bleeds." : "Your ward stands down.";
|
||||||
case "chaosShielded": return `${e.player} raises a FULL SHIELD and sits out the chaos.`;
|
case "chaosShielded": return `${e.player} raises a FULL SHIELD and sits out the chaos.`;
|
||||||
|
case "tableTalk": return `\u{1F4AC} ${e.player}: ${e.text}`;
|
||||||
case "dieRolled": return `\u{1F3B2} ${e.player ?? "The maze"} rolls a ${e.roll} — ${e.purpose}.`;
|
case "dieRolled": return `\u{1F3B2} ${e.player ?? "The maze"} rolls a ${e.roll} — ${e.purpose}.`;
|
||||||
case "pushed": return e.player
|
case "pushed": return e.player
|
||||||
? `${e.by} shoves ${e.player} down the corridor!`
|
? `${e.by} shoves ${e.player} down the corridor!`
|
||||||
@@ -320,12 +321,19 @@ class Net {
|
|||||||
case "catchUp":
|
case "catchUp":
|
||||||
this.catchUp = msg.steps;
|
this.catchUp = msg.steps;
|
||||||
break;
|
break;
|
||||||
case "events":
|
case "events": {
|
||||||
|
let talk = 0;
|
||||||
for (const e of msg.events as GameEvent[]) {
|
for (const e of msg.events as GameEvent[]) {
|
||||||
|
if (e.type === "tableTalk") talk++;
|
||||||
const line = humanize(e);
|
const line = humanize(e);
|
||||||
if (line) this.log = [...this.log, line];
|
if (line) this.log = [...this.log, line];
|
||||||
}
|
}
|
||||||
|
if (talk > 0) {
|
||||||
|
this.chatCount += talk;
|
||||||
|
if (this.roomId) this.markChatSeen();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case "transferCode":
|
case "transferCode":
|
||||||
this.transferCode = { code: msg.code, expiresAt: msg.expiresAt };
|
this.transferCode = { code: msg.code, expiresAt: msg.expiresAt };
|
||||||
break;
|
break;
|
||||||
@@ -342,17 +350,6 @@ class Net {
|
|||||||
if (this.roomId) this.markChatSeen();
|
if (this.roomId) this.markChatSeen();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "chatHistory": {
|
|
||||||
this.log = [
|
|
||||||
...this.log,
|
|
||||||
...(msg.messages as { player: string; text: string }[]).map(
|
|
||||||
(m) => `\u{1F4AC} ${m.player}: ${m.text}`,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
this.chatCount = msg.messages.length;
|
|
||||||
if (this.roomId) this.markChatSeen();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "stats": {
|
case "stats": {
|
||||||
this.stats = msg.stats;
|
this.stats = msg.stats;
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user