diff --git a/client/src/MediaControl.css b/client/src/MediaControl.css index 145e438..2d357fe 100644 --- a/client/src/MediaControl.css +++ b/client/src/MediaControl.css @@ -20,9 +20,9 @@ padding: 0; margin: 0; width: 5rem; - min-width: 5rem; height: 3.75rem; min-height: 3.75rem; + min-width: 5rem; background-color: #444; border-radius: 0.25rem; border: 2px dashed #666; /* Visual indicator for drop zone */ @@ -31,8 +31,8 @@ .MediaControlSpacer.Medium { width: 11.5em; height: 8.625em; - min-width: 11.5em; - min-height: 8.625em; + /* min-width: 11.5em; + min-height: 8.625em; */ } .MediaControl { @@ -42,8 +42,8 @@ left: 0; /* Start at left of container */ width: 5rem; height: 3.75rem; - min-width: 5rem; - min-height: 3.75rem; + min-width: 1.25rem; + min-height: 0.9375rem; z-index: 1200; border-radius: 0.25rem; } @@ -63,8 +63,8 @@ .MediaControl.Medium { width: 11.5em; height: 8.625em; - min-width: 11.5em; - min-height: 8.625em; + /* min-width: 11.5em; + min-height: 8.625em; */ } .MediaControl .Controls { diff --git a/client/src/PlayerList.tsx b/client/src/PlayerList.tsx index 1ae5e90..3680a64 100644 --- a/client/src/PlayerList.tsx +++ b/client/src/PlayerList.tsx @@ -193,15 +193,10 @@ const PlayerList: React.FC = () => {
Pick your color:
- {[ - { label: "Orange", value: "orange" }, - { label: "Red", value: "red" }, - { label: "White", value: "white" }, - { label: "Blue", value: "blue" }, - ].map((c) => ( - + + ))}
diff --git a/client/src/RoomView.css b/client/src/RoomView.css index 3cade8f..7f47253 100644 --- a/client/src/RoomView.css +++ b/client/src/RoomView.css @@ -93,7 +93,6 @@ justify-content: space-between; width: 25rem; max-width: 25rem; - overflow: hidden; z-index: 5000; } diff --git a/server/routes/games.ts b/server/routes/games.ts index a79d1b9..0b36466 100755 --- a/server/routes/games.ts +++ b/server/routes/games.ts @@ -99,7 +99,9 @@ const processTies = (players: Player[]): boolean => { const rev = slots.slice().reverse(); for (const slot of rev) { const s = slot || []; - if (s.length !== 1) { + // Only consider actual populated slots. Empty slots (holes) should be skipped. + if (s.length > 1) { + // real tie among multiple players in this slot ties = true; s.forEach((player: Player) => { player.orderRoll = 0; /* Ties have to be re-rolled */ @@ -107,12 +109,17 @@ const processTies = (players: Player[]): boolean => { player.orderStatus = `Tied for ${irstify(position)}`; player.tied = true; }); - } else if (s[0]) { + position += s.length; + } else if (s.length === 1 && s[0]) { + // single player in this slot - clear tie and record position s[0].tied = false; s[0].position = irstify(position); s[0].orderStatus = `Placed in ${irstify(position)}.`; + position += 1; + } else { + // empty slot - skip + continue; } - position += s.length; } return ties; @@ -155,10 +162,16 @@ const processGameOrder = (game: Game, player: Player, dice: number): any => { return B.order - A.order; }); - console.log(`Pre process ties: `, players); + console.log( + `${info}: Pre process ties: `, + players.reduce((acc, p) => ({ ...acc, [p.color as string]: p.orderRoll }), {}) + ); if (processTies(players)) { - console.log(`${info}: There are ties in player rolls:`, players); + console.log( + `${info}: There are ties in player rolls:`, + players.reduce((acc, p) => ({ ...acc, [p.color as string]: p.orderRoll }), {}) + ); sendUpdateToPlayers(game, { players: getFilteredPlayers(game), chat: game.chat, @@ -3380,14 +3393,33 @@ const ping = (session: Session) => { // Add new function to schedule recurring pings const schedulePing = (session: Session) => { + // Diagnostic logging to help detect multiple intervals being created + try { + console.log( + `${session.short}: schedulePing called for ${getName(session)} - existing pingInterval? ${!!session.pingInterval}` + ); + } catch (e) { + /* ignore logging errors */ + } + if (session.pingInterval) { - clearInterval(session.pingInterval); + // Clear any previous interval before creating a new one + try { + clearInterval(session.pingInterval); + } catch (e) { + console.warn(`${session.short}: Failed to clear previous pingInterval:`, e); + } } // Send ping every 10 seconds session.pingInterval = setInterval(() => { ping(session); }, 10000); + try { + console.log(`${session.short}: pingInterval started for ${getName(session)}`); + } catch (e) { + /* ignore logging errors */ + } }; // wsInactive not present in this refactor; no-op placeholder removed @@ -3764,6 +3796,7 @@ const sendError = (session: any, error: string): void => { }; const sendWarning = (session: any, warning: string): void => { + console.warn(`${session.short}: Warning: ${warning}`); try { session?.ws?.send(JSON.stringify({ type: "warning", warning })); } catch (e) { @@ -4220,6 +4253,22 @@ router.ws("/ws/:id", async (ws, req) => { // If there was a previous websocket and it's a different object, try to // close it to avoid stale sockets lingering in memory. if (previousWs && previousWs !== ws) { + // Clear any existing ping/keepAlive timers associated with the old socket + try { + if (session.pingInterval) { + clearInterval(session.pingInterval); + session.pingInterval = undefined; + console.log(`${short}: Cleared old pingInterval during reconnection for ${getName(session)}`); + } + if (session.keepAlive) { + clearTimeout(session.keepAlive); + session.keepAlive = undefined; + console.log(`${short}: Cleared old keepAlive during reconnection for ${getName(session)}`); + } + } catch (e) { + console.warn(`${short}: Error clearing old timers during reconnection:`, e); + } + // Clean up peer from audio registry before replacing WebSocket if (gameId in audio) { try { @@ -4249,11 +4298,10 @@ router.ws("/ws/:id", async (ws, req) => { let warning: string | void | undefined; let processed = true; - // If this is the first time the session attached a WebSocket, or if the - // websocket was just replaced (reconnect), send an initial consolidated - // snapshot so clients can render deterministically without needing to - // wait for a flurry of incremental game-update events. - sendInitialGameSnapshot(game, session); + // The initial-game snapshot is sent from the connection attach path to + // ensure it is only sent once per websocket lifecycle. Avoid sending it + // here from the message handler to prevent duplicate snapshots when a + // client sends messages during the attach/reconnect sequence. switch (incoming.type) { case "join": diff --git a/server/routes/games/constants.ts b/server/routes/games/constants.ts index 8b5e12c..b5b9169 100644 --- a/server/routes/games/constants.ts +++ b/server/routes/games/constants.ts @@ -15,6 +15,7 @@ export const debug = { export const all = `[ all ]`; export const info = `[ info ]`; export const todo = `[ todo ]`; +export const warn = `[ warn ]`; export const SEND_THROTTLE_MS = 50; export const INCOMING_GET_BATCH_MS = 20;