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:
Eric Wagoner
2026-08-16 16:00:51 -04:00
co-authored by Claude Fable 5
parent ef661c79ad
commit 5802e1df05
5 changed files with 60 additions and 16 deletions
-4
View File
@@ -19,7 +19,6 @@
// {type:"events", events} redacted for this recipient
// {type:"state", view, seq} redacted full view (after every change)
// {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:"error", message}
@@ -250,9 +249,6 @@ wss.on("connection", (socket) => {
if (room.state) {
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);
break;
}
+5
View File
@@ -223,6 +223,9 @@ export function addChat(room: Room, playerId: PlayerId, rawText: string): { text
const at = new Date().toISOString();
room.chat.push({ player: playerId, text, at });
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 });
return { text, at };
}
@@ -435,7 +438,9 @@ export function loadPersistedRooms(): void {
const r = startInMemory(room, line.expansion, line.colors, line.deckRev);
if ("error" in r) throw new Error(`replay start failed: ${r.error}`);
} 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.events.push({ type: "tableTalk", player: line.player, text: line.text });
} else if (line.kind === "command") {
if (!room.state) throw new Error("command before start in log");
const result = applyCommand(room.state, line.playerId, line.command as Command);