Building but users still not listing
This commit is contained in:
parent
720c0aa143
commit
81d366286a
@ -87,7 +87,7 @@ const Activity: React.FC<ActivityProps> = ({ keep, activity }) => {
|
||||
};
|
||||
|
||||
const Activities: React.FC = () => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [activities, setActivities] = useState<ActivityData[]>([]);
|
||||
const [turn, setTurn] = useState<TurnData | undefined>(undefined);
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
@ -103,15 +103,16 @@ const Activities: React.FC = () => {
|
||||
} else {
|
||||
request = fields;
|
||||
}
|
||||
ws?.send(
|
||||
JSON.stringify({
|
||||
type: "get",
|
||||
fields: request,
|
||||
})
|
||||
);
|
||||
sendJsonMessage({
|
||||
type: "get",
|
||||
fields: request,
|
||||
});
|
||||
};
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data) as { type: string; update?: Record<string, unknown> };
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update": {
|
||||
const ignoring: string[] = [],
|
||||
@ -153,21 +154,8 @@ const Activities: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage as EventListener);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage as EventListener);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, activities, turn, players, timestamp, color, state, fields]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -3,7 +3,7 @@ import Paper from "@mui/material/Paper";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import { formatDistanceToNow, formatDuration, intervalToDuration } from 'date-fns';
|
||||
import { formatDistanceToNow, formatDuration, intervalToDuration } from "date-fns";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import equal from "fast-deep-equal";
|
||||
|
||||
@ -34,10 +34,13 @@ const Chat: React.FC = () => {
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
const { ws, name, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, name, sendJsonMessage } = useContext(GlobalContext);
|
||||
const fields = useMemo(() => ["chat", "startTime"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`chat - game update`);
|
||||
@ -52,30 +55,13 @@ const Chat: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
}, [lastJsonMessage, chat, startTime]);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
sendJsonMessage({
|
||||
type: "get",
|
||||
fields,
|
||||
});
|
||||
}, [ws, fields, sendJsonMessage]);
|
||||
}, [fields, sendJsonMessage]);
|
||||
|
||||
const chatKeyPress = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
@ -84,13 +70,11 @@ const Chat: React.FC = () => {
|
||||
setAutoScroll(true);
|
||||
}
|
||||
|
||||
if (ws) {
|
||||
sendJsonMessage({ type: "chat", message: (event.target as HTMLInputElement).value });
|
||||
(event.target as HTMLInputElement).value = "";
|
||||
}
|
||||
sendJsonMessage({ type: "chat", message: (event.target as HTMLInputElement).value });
|
||||
(event.target as HTMLInputElement).value = "";
|
||||
}
|
||||
},
|
||||
[ws, setAutoScroll, autoScroll]
|
||||
[setAutoScroll, autoScroll]
|
||||
);
|
||||
|
||||
const chatScroll = (event: React.UIEvent<HTMLUListElement>) => {
|
||||
@ -217,9 +201,7 @@ const Chat: React.FC = () => {
|
||||
{item.color && <PlayerColor color={item.color} />}
|
||||
<ListItemText
|
||||
primary={message}
|
||||
secondary={
|
||||
item.color && formatDistanceToNow(new Date(item.date > now ? now : item.date))
|
||||
}
|
||||
secondary={item.color && formatDistanceToNow(new Date(item.date > now ? now : item.date))}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
@ -248,7 +230,7 @@ const Chat: React.FC = () => {
|
||||
const hours = duration.hours || 0;
|
||||
const minutes = duration.minutes || 0;
|
||||
const seconds = duration.seconds || 0;
|
||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
|
@ -12,15 +12,19 @@ import { GlobalContext } from "./GlobalContext";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
const ChooseCard: React.FC = () => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [turn, setTurn] = useState<any>(undefined);
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
const [state, setState] = useState<string | undefined>(undefined);
|
||||
const [cards, setCards] = useState<string[]>([]);
|
||||
const fields = useMemo(() => ["turn", "color", "state"], []);
|
||||
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`choose-card - game-update: `, data.update);
|
||||
@ -37,21 +41,7 @@ const ChooseCard: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, turn, color, state]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -21,13 +21,13 @@ interface PlayerItem {
|
||||
}
|
||||
|
||||
const GameOrder: React.FC = () => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [players, setPlayers] = useState<{ [key: string]: any }>({});
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
const fields = useMemo(() => ["players", "color"], []);
|
||||
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`GameOrder game-update: `, data.update);
|
||||
@ -41,21 +41,7 @@ const GameOrder: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, players, color]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
@ -66,12 +52,8 @@ const GameOrder: React.FC = () => {
|
||||
});
|
||||
}, [sendJsonMessage, fields]);
|
||||
|
||||
const sendMessage = (data: any) => {
|
||||
ws!.send(JSON.stringify(data));
|
||||
};
|
||||
|
||||
const rollClick = () => {
|
||||
sendMessage({ type: "roll" });
|
||||
sendJsonMessage({ type: "roll" });
|
||||
};
|
||||
|
||||
let hasRolled = true;
|
||||
|
@ -34,7 +34,7 @@ interface HandProps {
|
||||
}
|
||||
|
||||
const Hand: React.FC<HandProps> = ({ buildActive, setBuildActive, setCardActive }) => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [priv, setPriv] = useState<any>(undefined);
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
const [turn, setTurn] = useState<any>(undefined);
|
||||
@ -49,8 +49,12 @@ const Hand: React.FC<HandProps> = ({ buildActive, setBuildActive, setCardActive
|
||||
() => ["private", "turn", "color", "longestRoad", "largestArmy", "mostPorts", "mostDeveloped"],
|
||||
[]
|
||||
);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`hand - game-update: `, data.update);
|
||||
@ -79,21 +83,7 @@ const Hand: React.FC<HandProps> = ({ buildActive, setBuildActive, setCardActive
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, priv, turn, color, longestRoad, largestArmy, mostPorts, mostDeveloped]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -247,14 +247,17 @@ interface HouseRulesProps {
|
||||
}
|
||||
|
||||
const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive, setHouseRulesActive }) => {
|
||||
const { ws, name, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, name, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [rules, setRules] = useState<any>({});
|
||||
const [state, setState] = useState<any>({});
|
||||
const [gameState, setGameState] = useState<string>("");
|
||||
|
||||
const fields = useMemo(() => ["state", "rules"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`house-rules - game-update: `, data.update);
|
||||
@ -268,21 +271,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive, setHouseRules
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, rules, gameState]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
@ -477,7 +466,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive, setHouseRules
|
||||
),
|
||||
},
|
||||
].sort((a, b) => a.category.localeCompare(b.category)),
|
||||
[rules, setRules, state, ws, setRule, name, gameState]
|
||||
[rules, setRules, state, setRule, name, gameState]
|
||||
);
|
||||
|
||||
if (!houseRulesActive) {
|
||||
|
@ -1123,9 +1123,14 @@ const MediaAgent = (props: MediaAgentProps) => {
|
||||
if (media && joinStatus.status === "Not joined" && readyState === ReadyState.OPEN) {
|
||||
console.log(`media-agent - Initiating media join for ${session.name}`);
|
||||
setJoinStatus({ status: "Joining" });
|
||||
sendJsonMessage({ type: "join", data: {} });
|
||||
sendJsonMessage({
|
||||
type: "join",
|
||||
data: {
|
||||
has_media: session.has_media !== false, // Default to true for backward compatibility
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [media, joinStatus.status, sendJsonMessage, readyState, session.name]);
|
||||
}, [media, joinStatus.status, sendJsonMessage, readyState, session.name, session.has_media]);
|
||||
|
||||
// Update local peer in peers list
|
||||
useEffect(() => {
|
||||
|
@ -1,46 +0,0 @@
|
||||
import React, { useState, useContext, useEffect, useRef } from "react";
|
||||
import { GlobalContext } from "./GlobalContext";
|
||||
import "./PingPong.css";
|
||||
|
||||
const PingPong: React.FC = () => {
|
||||
const [count, setCount] = useState<number>(0);
|
||||
const global = useContext(GlobalContext);
|
||||
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data as string);
|
||||
switch (data.type) {
|
||||
case "ping":
|
||||
if (global.ws) {
|
||||
global.ws.send(JSON.stringify({ type: "pong", timestamp: data.ping }));
|
||||
}
|
||||
setCount(count + 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!global.ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
global.ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
global.ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [global.ws, refWsMessage]);
|
||||
|
||||
return (
|
||||
<div className="PingPong">
|
||||
Game {global.gameId}: {global.name} {global.ws ? "has socket" : "no socket"} {count} pings
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { PingPong };
|
@ -16,13 +16,7 @@ type PlacardProps = {
|
||||
};
|
||||
|
||||
const Placard: React.FC<PlacardProps> = ({ type, disabled, count, buildActive, setBuildActive, className, sx }) => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const sendMessage = useCallback(
|
||||
(data: Record<string, unknown>) => {
|
||||
sendJsonMessage(data);
|
||||
},
|
||||
[sendJsonMessage]
|
||||
);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
|
||||
const dismissClicked = () => {
|
||||
setBuildActive && setBuildActive(false);
|
||||
@ -37,19 +31,19 @@ const Placard: React.FC<PlacardProps> = ({ type, disabled, count, buildActive, s
|
||||
};
|
||||
|
||||
const roadClicked = () => {
|
||||
sendMessage({ type: "buy-road" });
|
||||
sendJsonMessage({ type: "buy-road" });
|
||||
setBuildActive && setBuildActive(false);
|
||||
};
|
||||
const settlementClicked = () => {
|
||||
sendMessage({ type: "buy-settlement" });
|
||||
sendJsonMessage({ type: "buy-settlement" });
|
||||
setBuildActive && setBuildActive(false);
|
||||
};
|
||||
const cityClicked = () => {
|
||||
sendMessage({ type: "buy-city" });
|
||||
sendJsonMessage({ type: "buy-city" });
|
||||
setBuildActive && setBuildActive(false);
|
||||
};
|
||||
const developmentClicked = () => {
|
||||
sendMessage({ type: "buy-development" });
|
||||
sendJsonMessage({ type: "buy-development" });
|
||||
setBuildActive && setBuildActive(false);
|
||||
};
|
||||
|
||||
|
@ -60,38 +60,35 @@ const PlayerList: React.FC = () => {
|
||||
}
|
||||
const data: any = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "players": {
|
||||
type RoomStateData = {
|
||||
participants: Player[];
|
||||
};
|
||||
const room_state = data as RoomStateData;
|
||||
console.log(`Players - room_state`, room_state.participants);
|
||||
room_state.participants.forEach((player) => {
|
||||
player.local = player.session_id === session.id;
|
||||
});
|
||||
room_state.participants.sort(sortPlayers);
|
||||
setPlayers(room_state.participants);
|
||||
// Initialize peers with remote mute/video state
|
||||
setPeers((prevPeers) => {
|
||||
const updated: Record<string, Peer> = { ...prevPeers };
|
||||
room_state.participants.forEach((player) => {
|
||||
// Only update remote peers, never overwrite local peer object
|
||||
if (!player.local && updated[player.session_id]) {
|
||||
updated[player.session_id] = {
|
||||
...updated[player.session_id],
|
||||
muted: player.muted ?? false,
|
||||
video_on: player.video_on ?? true,
|
||||
};
|
||||
}
|
||||
case "game-update": {
|
||||
console.log(`PlayerList - game-update:`, data.update);
|
||||
|
||||
// Handle participants list
|
||||
if ("participants" in data.update && data.update.participants) {
|
||||
const participantsList: Player[] = data.update.participants;
|
||||
console.log(`PlayerList - participants:`, participantsList);
|
||||
|
||||
participantsList.forEach((player) => {
|
||||
player.local = player.session_id === session?.id;
|
||||
});
|
||||
participantsList.sort(sortPlayers);
|
||||
setPlayers(participantsList);
|
||||
|
||||
// Initialize peers with remote mute/video state
|
||||
setPeers((prevPeers) => {
|
||||
const updated: Record<string, Peer> = { ...prevPeers };
|
||||
participantsList.forEach((player) => {
|
||||
// Only update remote peers, never overwrite local peer object
|
||||
if (!player.local && updated[player.session_id]) {
|
||||
updated[player.session_id] = {
|
||||
...updated[player.session_id],
|
||||
muted: player.muted ?? false,
|
||||
video_on: player.video_on ?? true,
|
||||
};
|
||||
}
|
||||
});
|
||||
return updated;
|
||||
});
|
||||
return updated;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "update_name": {
|
||||
// Update local session name immediately
|
||||
if (data && typeof data.name === "string") {
|
||||
session.name = data.name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -99,12 +96,12 @@ const PlayerList: React.FC = () => {
|
||||
// Update peer state in peers, but do not override local mute
|
||||
setPeers((prevPeers) => {
|
||||
const updated = { ...prevPeers };
|
||||
const peerId = data.peer_id;
|
||||
const peerId = data.data?.peer_id || data.peer_id;
|
||||
if (peerId && updated[peerId]) {
|
||||
updated[peerId] = {
|
||||
...updated[peerId],
|
||||
muted: data.muted,
|
||||
video_on: data.video_on,
|
||||
muted: data.data?.muted ?? data.muted,
|
||||
video_on: data.data?.video_on ?? data.video_on,
|
||||
};
|
||||
}
|
||||
return updated;
|
||||
@ -117,11 +114,13 @@ const PlayerList: React.FC = () => {
|
||||
}, [lastJsonMessage, session, sortPlayers, setPeers, setPlayers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (players !== null) {
|
||||
if (players !== null || !sendJsonMessage) {
|
||||
return;
|
||||
}
|
||||
// Request participants list
|
||||
sendJsonMessage({
|
||||
type: "list_Players",
|
||||
type: "get",
|
||||
fields: ["participants"],
|
||||
});
|
||||
}, [players, sendJsonMessage]);
|
||||
|
||||
|
@ -133,7 +133,7 @@ interface PlayersStatusProps {
|
||||
}
|
||||
|
||||
const PlayersStatus: React.FC<PlayersStatusProps> = ({ active }) => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [players, setPlayers] = useState<any>(undefined);
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
const [largestArmy, setLargestArmy] = useState<string | undefined>(undefined);
|
||||
@ -141,8 +141,12 @@ const PlayersStatus: React.FC<PlayersStatusProps> = ({ active }) => {
|
||||
const [mostPorts, setMostPorts] = useState<string | undefined>(undefined);
|
||||
const [mostDeveloped, setMostDeveloped] = useState<string | undefined>(undefined);
|
||||
const fields = useMemo(() => ["players", "color", "longestRoad", "largestArmy", "mostPorts", "mostDeveloped"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`players-status - game-update: `, data.update);
|
||||
@ -168,21 +172,7 @@ const PlayersStatus: React.FC<PlayersStatusProps> = ({ active }) => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, players, color, longestRoad, largestArmy, mostPorts, mostDeveloped]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -11,13 +11,16 @@ import { GlobalContext } from "./GlobalContext";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
|
||||
|
||||
const SelectPlayer: React.FC = () => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [turn, setTurn] = useState<any>(undefined);
|
||||
const [color, setColor] = useState<string | undefined>(undefined);
|
||||
const fields = useMemo(() => ["turn", "color"], []);
|
||||
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`select-players - game-update: `, data.update);
|
||||
@ -31,21 +34,7 @@ const SelectPlayer: React.FC = () => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, turn, color]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -16,13 +16,16 @@ interface ViewCardProps {
|
||||
}
|
||||
|
||||
const ViewCard: React.FC<ViewCardProps> = ({ cardActive, setCardActive }) => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [priv, setPriv] = useState<any>(undefined);
|
||||
const [turns, setTurns] = useState<number>(0);
|
||||
const [rules, setRules] = useState<any>({});
|
||||
const fields = useMemo(() => ["private", "turns", "rules"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data = JSON.parse(event.data as string);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`view-card - game update`);
|
||||
@ -39,21 +42,7 @@ const ViewCard: React.FC<ViewCardProps> = ({ cardActive, setCardActive }) => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, priv, turns, rules]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -17,12 +17,16 @@ interface WinnerProps {
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const Winner: React.FC<WinnerProps> = ({ winnerDismissed, setWinnerDismissed }) => {
|
||||
const { ws, sendJsonMessage } = useContext(GlobalContext);
|
||||
const { lastJsonMessage, sendJsonMessage } = useContext(GlobalContext);
|
||||
const [winner, setWinner] = useState<any>(undefined);
|
||||
const [state, setState] = useState<string | undefined>(undefined);
|
||||
const fields = useMemo(() => ["winner", "state"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
const data: { type: string; update: any } = JSON.parse(event.data);
|
||||
useEffect(() => {
|
||||
if (!lastJsonMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = lastJsonMessage;
|
||||
switch (data.type) {
|
||||
case "game-update":
|
||||
console.log(`winner - game update`, data.update);
|
||||
@ -40,21 +44,7 @@ const Winner: React.FC<WinnerProps> = ({ winnerDismissed, setWinnerDismissed })
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
const refWsMessage = useRef(onWsMessage);
|
||||
useEffect(() => {
|
||||
refWsMessage.current = onWsMessage;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
const cbMessage = (e: MessageEvent) => refWsMessage.current(e);
|
||||
ws.addEventListener("message", cbMessage);
|
||||
return () => {
|
||||
ws.removeEventListener("message", cbMessage);
|
||||
};
|
||||
}, [ws, refWsMessage]);
|
||||
}, [lastJsonMessage, winner, state, setWinnerDismissed]);
|
||||
useEffect(() => {
|
||||
if (!sendJsonMessage) {
|
||||
return;
|
||||
|
@ -1163,6 +1163,7 @@ const setPlayerName = (game: Game, session: Session, name: string): string | und
|
||||
});
|
||||
sendUpdateToPlayers(game, {
|
||||
players: getFilteredPlayers(game),
|
||||
participants: getParticipants(game),
|
||||
unselected: getFilteredUnselected(game),
|
||||
chat: game.chat,
|
||||
});
|
||||
@ -1282,6 +1283,7 @@ const setPlayerColor = (game: Game, session: Session, color: string): string | u
|
||||
|
||||
const update: any = {
|
||||
players: getFilteredPlayers(game),
|
||||
participants: getParticipants(game),
|
||||
chat: game.chat,
|
||||
};
|
||||
|
||||
@ -3407,19 +3409,79 @@ const resetDisconnectCheck = (_game: any, req: any): void => {
|
||||
//req.disconnectCheck = setTimeout(() => { wsInactive(game, req) }, 20000);
|
||||
};
|
||||
|
||||
const join = (peers: any, session: any, { hasVideo, hasAudio }: { hasVideo?: boolean; hasAudio?: boolean }): void => {
|
||||
const join = (peers: any, session: any, { hasVideo, hasAudio, has_media }: { hasVideo?: boolean; hasAudio?: boolean; has_media?: boolean }): void => {
|
||||
const ws = session.ws;
|
||||
|
||||
if (!session.name) {
|
||||
console.error(`${session.id}: <- join - No name set yet. Audio not available.`);
|
||||
ws.send(JSON.stringify({
|
||||
type: "join_status",
|
||||
status: "Error",
|
||||
message: "No name set yet. Audio not available."
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${session.id}: <- join - ${session.name}`);
|
||||
console.log(`${all}: -> addPeer - ${session.name}`);
|
||||
|
||||
// Determine media capability - prefer has_media if provided, otherwise derive from hasVideo/hasAudio
|
||||
const peerHasMedia = has_media !== undefined ? has_media : (hasVideo || hasAudio);
|
||||
|
||||
if (session.name in peers) {
|
||||
console.log(`${session.id}:${session.name} - Already joined to Audio.`);
|
||||
console.log(`${session.id}:${session.name} - Already joined to Audio, updating WebSocket reference.`);
|
||||
|
||||
// Update the WebSocket reference in case of reconnection
|
||||
peers[session.name].ws = ws;
|
||||
peers[session.name].has_media = peerHasMedia;
|
||||
peers[session.name].hasAudio = hasAudio;
|
||||
peers[session.name].hasVideo = hasVideo;
|
||||
|
||||
// Send join status to reconnected client
|
||||
ws.send(JSON.stringify({
|
||||
type: "join_status",
|
||||
status: "Joined",
|
||||
message: "Reconnected"
|
||||
}));
|
||||
|
||||
// Notify the reconnected client about all existing peers
|
||||
for (const peer in peers) {
|
||||
if (peer === session.name) continue; // Skip self
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "addPeer",
|
||||
data: {
|
||||
peer_id: peer,
|
||||
peer_name: peer,
|
||||
has_media: peers[peer].has_media,
|
||||
should_create_offer: true,
|
||||
hasAudio: peers[peer].hasAudio,
|
||||
hasVideo: peers[peer].hasVideo,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Notify all other peers about the reconnected peer (with updated connection)
|
||||
for (const peer in peers) {
|
||||
if (peer === session.name) continue; // Skip self
|
||||
|
||||
peers[peer].ws.send(
|
||||
JSON.stringify({
|
||||
type: "addPeer",
|
||||
data: {
|
||||
peer_id: session.name,
|
||||
peer_name: session.name,
|
||||
has_media: peerHasMedia,
|
||||
should_create_offer: false,
|
||||
hasAudio,
|
||||
hasVideo,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3430,6 +3492,8 @@ const join = (peers: any, session: any, { hasVideo, hasAudio }: { hasVideo?: boo
|
||||
type: "addPeer",
|
||||
data: {
|
||||
peer_id: session.name,
|
||||
peer_name: session.name,
|
||||
has_media: peers[session.name]?.has_media ?? peerHasMedia,
|
||||
should_create_offer: false,
|
||||
hasAudio,
|
||||
hasVideo,
|
||||
@ -3443,6 +3507,8 @@ const join = (peers: any, session: any, { hasVideo, hasAudio }: { hasVideo?: boo
|
||||
type: "addPeer",
|
||||
data: {
|
||||
peer_id: peer,
|
||||
peer_name: peer,
|
||||
has_media: peers[peer].has_media,
|
||||
should_create_offer: true,
|
||||
hasAudio: peers[peer].hasAudio,
|
||||
hasVideo: peers[peer].hasVideo,
|
||||
@ -3456,7 +3522,15 @@ const join = (peers: any, session: any, { hasVideo, hasAudio }: { hasVideo?: boo
|
||||
ws,
|
||||
hasAudio,
|
||||
hasVideo,
|
||||
has_media: peerHasMedia,
|
||||
};
|
||||
|
||||
/* Send join success status */
|
||||
ws.send(JSON.stringify({
|
||||
type: "join_status",
|
||||
status: "Joined",
|
||||
message: "Successfully joined"
|
||||
}));
|
||||
};
|
||||
|
||||
const part = (peers: any, session: any): void => {
|
||||
@ -3483,13 +3557,19 @@ const part = (peers: any, session: any): void => {
|
||||
peers[peer].ws.send(
|
||||
JSON.stringify({
|
||||
type: "removePeer",
|
||||
data: { peer_id: session.name },
|
||||
data: {
|
||||
peer_id: session.name,
|
||||
peer_name: session.name
|
||||
},
|
||||
})
|
||||
);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "removePeer",
|
||||
data: { peer_id: session.name },
|
||||
data: {
|
||||
peer_id: peer,
|
||||
peer_name: peer
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -3856,6 +3936,49 @@ const getFilteredPlayers = (game: any): Record<string, any> => {
|
||||
return filtered;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get participants list for the game room
|
||||
* Uses the reusable room helper and adds game-specific data (color)
|
||||
*
|
||||
* This demonstrates how to extend the base participant list with app-specific data
|
||||
*/
|
||||
const getParticipants = (game: any): any[] => {
|
||||
// Use the reusable room helper for base participant data
|
||||
// If you were using the new architecture, this would be:
|
||||
// import { getParticipants as getBaseParticipants } from './room/helpers';
|
||||
// const baseParticipants = getBaseParticipants(game.sessions);
|
||||
|
||||
const participants: any[] = [];
|
||||
for (let id in game.sessions) {
|
||||
const session = game.sessions[id];
|
||||
if (!session) continue;
|
||||
|
||||
// Base participant data (reusable across any application)
|
||||
const baseParticipant = {
|
||||
name: session.name || null,
|
||||
session_id: session.id,
|
||||
live: session.live || false,
|
||||
protected: session.protected || false,
|
||||
has_media: session.has_media !== false,
|
||||
bot_run_id: session.bot_run_id || null,
|
||||
bot_provider_id: session.bot_provider_id || null,
|
||||
bot_instance_id: session.bot_instance_id || null,
|
||||
muted: session.muted || false,
|
||||
video_on: session.video_on !== false,
|
||||
};
|
||||
|
||||
// Game-specific data (in metadata layer)
|
||||
// This is the ONLY game-specific code in this function
|
||||
const gameSpecific = {
|
||||
color: session.color || null, // Game-specific: player color
|
||||
// In the new architecture, this would be: session.metadata?.color
|
||||
};
|
||||
|
||||
participants.push({ ...baseParticipant, ...gameSpecific });
|
||||
}
|
||||
return participants;
|
||||
};
|
||||
|
||||
const calculatePoints = (game: any, update: any): void => {
|
||||
if (game.state === "winner") {
|
||||
return;
|
||||
@ -4225,6 +4348,16 @@ 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) {
|
||||
// Clean up peer from audio registry before replacing WebSocket
|
||||
if (gameId in audio) {
|
||||
try {
|
||||
part(audio[gameId], session);
|
||||
console.log(`${short}: Cleaned up peer ${session.name} from audio registry during reconnection`);
|
||||
} catch (e) {
|
||||
console.warn(`${short}: Error cleaning up peer during reconnection:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
previousWs.close();
|
||||
} catch (e) {
|
||||
@ -4282,7 +4415,11 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
|
||||
message = JSON.stringify({
|
||||
type: "iceCandidate",
|
||||
data: { peer_id: getName(session), candidate: candidate },
|
||||
data: {
|
||||
peer_id: getName(session),
|
||||
peer_name: getName(session),
|
||||
candidate: candidate
|
||||
},
|
||||
}) as any;
|
||||
|
||||
if (peer_id in audio[gameId]) {
|
||||
@ -4312,7 +4449,11 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
);
|
||||
message = JSON.stringify({
|
||||
type: "sessionDescription",
|
||||
data: { peer_id: getName(session), session_description: session_description },
|
||||
data: {
|
||||
peer_id: getName(session),
|
||||
peer_name: getName(session),
|
||||
session_description: session_description
|
||||
},
|
||||
}) as any;
|
||||
if (peer_id in audio[gameId]) {
|
||||
try {
|
||||
@ -4350,7 +4491,12 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
|
||||
const messagePayload = JSON.stringify({
|
||||
type: "peer_state_update",
|
||||
data: { peer_id: session.name, muted, video_on },
|
||||
data: {
|
||||
peer_id: session.name,
|
||||
peer_name: session.name,
|
||||
muted,
|
||||
video_on
|
||||
},
|
||||
});
|
||||
|
||||
// Send to all other peers
|
||||
@ -4488,6 +4634,9 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
case "players":
|
||||
batchedUpdate.players = getFilteredPlayers(game);
|
||||
break;
|
||||
case "participants":
|
||||
batchedUpdate.participants = getParticipants(game);
|
||||
break;
|
||||
case "color":
|
||||
console.log(`${session.id}: -> Returning color as ${session.color} for ${getName(session)}`);
|
||||
batchedUpdate.color = session.color;
|
||||
@ -4771,6 +4920,7 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
if (session.name) {
|
||||
sendUpdateToPlayers(game, {
|
||||
players: getFilteredPlayers(game),
|
||||
participants: getParticipants(game),
|
||||
unselected: getFilteredUnselected(game),
|
||||
});
|
||||
}
|
||||
@ -5326,7 +5476,13 @@ router.get("/", (req, res /*, next*/) => {
|
||||
|
||||
// Mark this response as coming from the backend API to aid debugging
|
||||
res.setHeader("X-Backend", "games");
|
||||
return res.status(200).send({ player: playerId });
|
||||
return res.status(200).send({
|
||||
id: playerId,
|
||||
player: playerId,
|
||||
name: null,
|
||||
lobbies: [],
|
||||
has_media: true // Default to true for regular users
|
||||
});
|
||||
});
|
||||
|
||||
router.post("/:id?", async (req, res /*, next*/) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user