1
0

Fixing dulicate calls

This commit is contained in:
James Ketr 2025-10-09 12:31:56 -07:00
parent 4218177bc7
commit 0d1024ff61
5 changed files with 74 additions and 32 deletions

View File

@ -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 {

View File

@ -193,15 +193,10 @@ const PlayerList: React.FC = () => {
<div style={{ marginTop: 8, width: "100%" }}>
<div style={{ marginBottom: 6, fontSize: "0.9em" }}>Pick your color:</div>
<div style={{ display: "flex", gap: 8 }}>
{[
{ label: "Orange", value: "orange" },
{ label: "Red", value: "red" },
{ label: "White", value: "white" },
{ label: "Blue", value: "blue" },
].map((c) => (
<button
key={c.value}
style={{
{["orange", "red", "white", "blue"].map((c) => (
<Box
key={c}
sx={{
display: "flex",
alignItems: "center",
gap: 8,
@ -213,12 +208,11 @@ const PlayerList: React.FC = () => {
}}
onClick={() => {
if (!sendJsonMessage) return;
sendJsonMessage({ type: "set", field: "color", value: c.value });
sendJsonMessage({ type: "set", field: "color", value: c[0].toUpperCase() });
}}
>
<PlayerColor color={c.value} />
<div style={{ fontSize: "0.9em" }}>{c.label}</div>
</button>
<PlayerColor color={c} />
</Box>
))}
</div>
</div>

View File

@ -93,7 +93,6 @@
justify-content: space-between;
width: 25rem;
max-width: 25rem;
overflow: hidden;
z-index: 5000;
}

View File

@ -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) {
// 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":

View File

@ -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;