The host may let the Peanut Gallery talk
A gallery line wears the eye and a name that no wizard at the table holds; the host opens or hushes it from the lobby or the mast, on the ledger, and the gallery is reined per address. 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
f68b3cef06
commit
13a6fc2603
@@ -8,7 +8,8 @@
|
||||
// {type:"makeTransfer"} mint a seat-transfer phrase
|
||||
// {type:"claimTransfer", code} claim a seat on a new device
|
||||
// {type:"catchUp", sinceSeq} replay of moves missed while away
|
||||
// {type:"chat", text} table talk to the room
|
||||
// {type:"chat", text, name?} table talk to the room (name: from the gallery)
|
||||
// {type:"galleryTalk", on} host lets the Peanut Gallery talk, or not
|
||||
// {type:"feedback", happened, expected} a surprise report, pinned to room+seq
|
||||
// {type:"myFeedback", seats} your reports + the wizards' replies
|
||||
// {type:"rollDie"} the tabletop D4, published as talk
|
||||
@@ -58,6 +59,9 @@ import {
|
||||
runningRooms,
|
||||
addAutomaton,
|
||||
addChat,
|
||||
addGalleryChat,
|
||||
setGalleryTalk,
|
||||
GALLERY_MARK,
|
||||
driveOneAutomaton,
|
||||
makeTransferCode,
|
||||
redactFor,
|
||||
@@ -151,6 +155,8 @@ const challengesPerAddress = new SlidingLimit(3, 60 * 60 * 1000);
|
||||
const KEEPER_ZONE = process.env.WIZWAR_KEEPER_TZ ?? "America/New_York";
|
||||
const PUBLIC_URL = (process.env.WIZWAR_PUBLIC_URL ?? "https://wizwar.kestrelsnest.social").replace(/\/$/, "");
|
||||
const reportsPerAddress = new SlidingLimit(6, 60 * 60 * 1000);
|
||||
/** The gallery talks under a tighter rein than the table: no seat, no token, one address. */
|
||||
const galleryPerAddress = new SlidingLimit(30, 10 * 60 * 1000);
|
||||
const MAX_COMMAND_BYTES = 16384; // serialized game command
|
||||
const MAX_MYGAMES_SEATS = 50; // seats checked per myGames request
|
||||
const CATCHUP_COOLDOWN_MS = 3000; // full-game replays are CPU-heavy
|
||||
@@ -159,7 +165,8 @@ const NAME_MAX = 24;
|
||||
|
||||
/** Player/room names: printable, trimmed, bounded. */
|
||||
function cleanName(raw: unknown): string {
|
||||
return String(raw ?? "").replace(/[\u0000-\u001f\u007f]/g, "").trim().slice(0, NAME_MAX);
|
||||
// The gallery's mark is not a name anyone may take.
|
||||
return String(raw ?? "").replace(/[\u0000-\u001f\u007f]/g, "").replaceAll(GALLERY_MARK, "").trim().slice(0, NAME_MAX);
|
||||
}
|
||||
|
||||
const port = Number(process.env.PORT ?? 8787);
|
||||
@@ -597,6 +604,7 @@ function roomInfo(room: Room) {
|
||||
challenge: room.challenge ?? null,
|
||||
keeper: KEEPER,
|
||||
keeperZone: KEEPER_ZONE,
|
||||
galleryTalk: room.galleryTalk,
|
||||
colors: Object.fromEntries(room.colorChoices),
|
||||
bots: Object.fromEntries(
|
||||
// A mystery machine keeps its mood only while the game lives: once it
|
||||
@@ -972,12 +980,28 @@ wss.on("connection", (socket, req) => {
|
||||
}
|
||||
case "chat": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room || !session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
if (!room) return send(socket, { type: "error", message: "not in a room" });
|
||||
if (session.spectator) {
|
||||
if (!galleryPerAddress.allow(session.address)) return send(socket, { type: "error", message: "the gallery has said plenty for now" });
|
||||
const said = addGalleryChat(room, cleanName(msg.name), String(msg.text ?? ""));
|
||||
if ("error" in said) return send(socket, { type: "error", message: said.error });
|
||||
broadcast(room, () => ({ type: "chat", player: said.player, text: said.text, at: said.at }));
|
||||
break;
|
||||
}
|
||||
if (!session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
const result = addChat(room, session.playerId, String(msg.text ?? ""));
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
broadcast(room, () => ({ type: "chat", player: session.playerId, text: result.text, at: result.at }));
|
||||
break;
|
||||
}
|
||||
case "galleryTalk": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room || !session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
const set = setGalleryTalk(room, session.playerId, msg.on === true);
|
||||
if ("error" in set) return send(socket, { type: "error", message: set.error });
|
||||
broadcastRoomState(room);
|
||||
break;
|
||||
}
|
||||
case "makeTransfer": {
|
||||
const room = session.roomId ? getRoom(session.roomId) : undefined;
|
||||
if (!room || !session.playerId) return send(socket, { type: "error", message: "not in a room" });
|
||||
|
||||
Reference in New Issue
Block a user