1
0

Meida working

This commit is contained in:
James Ketr 2025-10-10 20:01:42 -07:00
parent 579632c293
commit a586f3b491
3 changed files with 123 additions and 113 deletions

View File

@ -163,97 +163,100 @@ const PlayerList: React.FC = () => {
>
<MediaAgent {...{ session, peers, setPeers }} />
<List className="PlayerSelector">
{players?.map((player) => (
<Box
key={player.session_id}
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
className={`PlayerEntry ${player.local ? "PlayerSelf" : ""}`}
>
<Box>
<Box style={{ display: "flex-wrap", alignItems: "center", justifyContent: "space-between" }}>
<Box style={{ display: "flex-wrap", alignItems: "center" }}>
<div className="Name">{player.name ? player.name : player.session_id}</div>
{player.protected && (
<div
style={{ marginLeft: 8, fontSize: "0.8em", color: "#a00" }}
title="This name is protected with a password"
>
🔒
</div>
)}
{player.bot_instance_id && (
<div style={{ marginLeft: 8, fontSize: "0.8em", color: "#00a" }} title="This is a bot">
🤖
</div>
)}
{players?.map((player) => {
const peerObj = peers[player.session_id] || peers[player.name];
return (
<Box
key={player.session_id}
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
className={`PlayerEntry ${player.local ? "PlayerSelf" : ""}`}
>
<Box>
<Box style={{ display: "flex-wrap", alignItems: "center", justifyContent: "space-between" }}>
<Box style={{ display: "flex-wrap", alignItems: "center" }}>
<div className="Name">{player.name ? player.name : player.session_id}</div>
{player.protected && (
<div
style={{ marginLeft: 8, fontSize: "0.8em", color: "#a00" }}
title="This name is protected with a password"
>
🔒
</div>
)}
{player.bot_instance_id && (
<div style={{ marginLeft: 8, fontSize: "0.8em", color: "#00a" }} title="This is a bot">
🤖
</div>
)}
</Box>
</Box>
{player.name && !player.live && <div className="NoNetwork"></div>}
</Box>
{player.name && !player.live && <div className="NoNetwork"></div>}
</Box>
{player.name && player.live && peers[player.session_id] && (player.local || player.has_media !== false) ? (
<>
<MediaControl
sx={{ border: "3px solid blue" }}
className="Medium"
key={player.session_id}
peer={peers[player.session_id]}
isSelf={player.local}
sendJsonMessage={player.local ? sendJsonMessage : undefined}
remoteAudioMuted={peers[player.session_id].muted}
remoteVideoOff={peers[player.session_id].video_on === false}
/>
{player.name && player.live && peerObj && (player.local || player.has_media !== false) ? (
<>
<MediaControl
sx={{ border: "3px solid blue" }}
className="Medium"
key={player.session_id}
peer={peerObj}
isSelf={player.local}
sendJsonMessage={player.local ? sendJsonMessage : undefined}
remoteAudioMuted={peerObj?.muted}
remoteVideoOff={peerObj?.video_on === false}
/>
{/* If this is the local player and they haven't picked a color, show a picker */}
{player.local && player.color === "unassigned" && (
<div style={{ marginTop: 8, width: "100%" }}>
<div style={{ marginBottom: 6, fontSize: "0.9em" }}>Pick your color:</div>
<div style={{ display: "flex", gap: 8 }}>
{["orange", "red", "white", "blue"].map((c) => (
<Box
key={c}
sx={{
display: "flex",
alignItems: "center",
gap: 8,
padding: "6px 8px",
borderRadius: 6,
border: "1px solid #ccc",
background: "#fff",
cursor: sendJsonMessage ? "pointer" : "not-allowed",
}}
onClick={() => {
if (!sendJsonMessage) return;
sendJsonMessage({ type: "set", field: "color", value: c[0].toUpperCase() });
}}
>
<PlayerColor color={c} />
</Box>
))}
{/* If this is the local player and they haven't picked a color, show a picker */}
{player.local && player.color === "unassigned" && (
<div style={{ marginTop: 8, width: "100%" }}>
<div style={{ marginBottom: 6, fontSize: "0.9em" }}>Pick your color:</div>
<div style={{ display: "flex", gap: 8 }}>
{["orange", "red", "white", "blue"].map((c) => (
<Box
key={c}
sx={{
display: "flex",
alignItems: "center",
gap: 8,
padding: "6px 8px",
borderRadius: 6,
border: "1px solid #ccc",
background: "#fff",
cursor: sendJsonMessage ? "pointer" : "not-allowed",
}}
onClick={() => {
if (!sendJsonMessage) return;
sendJsonMessage({ type: "set", field: "color", value: c[0].toUpperCase() });
}}
>
<PlayerColor color={c} />
</Box>
))}
</div>
</div>
</div>
)}
</>
) : player.name && player.live && player.has_media === false ? (
<div
className="Video fade-in"
style={{
background: "#333",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "100%",
fontSize: "14px",
}}
>
💬 Chat Only
</div>
) : (
<video className="Video"></video>
)}
</Box>
))}
)}
</>
) : player.name && player.live && player.has_media === false ? (
<div
className="Video fade-in"
style={{
background: "#333",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "100%",
fontSize: "14px",
}}
>
💬 Chat Only
</div>
) : (
<video className="Video"></video>
)}
</Box>
);
})}
</List>
</Paper>
</Box>

View File

@ -1222,13 +1222,13 @@ const setPlayerName = (game: Game, session: Session, name: string): string | und
message = `${name} has rejoined the lobby.`;
}
session.name = name;
if (session.ws && game.id in audio && session.id in audio[game.id]) {
webrtcPart(audio[game.id], session);
if (session.ws && game.id in audio && session.id in audio[game.id]!) {
webrtcPart(audio[game.id]!, session);
}
} else {
message = `${session.name} has changed their name to ${name}.`;
message = `${session.name} hs changed their name to ${name}.`;
if (session.ws && game.id in audio) {
webrtcPart(audio[game.id], session);
webrtcPart(audio[game.id]!, session);
}
}
}
@ -1245,7 +1245,7 @@ const setPlayerName = (game: Game, session: Session, name: string): string | und
}
if (session.ws && session.hasAudio) {
webrtcJoin(audio[game.id], session);
webrtcJoin(audio[game.id]!, session);
}
console.log(`${info}: ${message}`);
addChatMessage(game, null, message);
@ -3726,7 +3726,7 @@ router.ws("/ws/:id", async (ws, req) => {
/* ignore logging errors */
}
if (!(gameId in audio)) {
audio[gameId] = {}; /* List of peer sockets using session.id as index. */
audio[gameId] = {}; /* List of peer sockets using session.id as index. */
console.log(`${short}: Game ${gameId} - New Game Audio`);
} else {
console.log(`${short}: Game ${gameId} - Already has Audio`);
@ -3822,7 +3822,7 @@ router.ws("/ws/:id", async (ws, req) => {
/* Cleanup any voice channels */
if (gameId in audio) {
try {
webrtcPart(audio[gameId], session);
webrtcPart(audio[gameId]!, session);
} catch (e) {
console.warn(`${short}: Error during part():`, e);
}
@ -3932,7 +3932,7 @@ router.ws("/ws/:id", async (ws, req) => {
// Clean up peer from audio registry before replacing WebSocket
if (gameId in audio) {
try {
webrtcPart(audio[gameId], session);
webrtcPart(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);
@ -3968,11 +3968,11 @@ router.ws("/ws/:id", async (ws, req) => {
// Accept either legacy `config`, newer `data`, or flat payloads where
// the client sent fields at the top level (normalizeIncoming will
// populate `data` with the parsed object in that case).
webrtcJoin(audio[gameId], session);
webrtcJoin(audio[gameId]!, session);
break;
case "part":
webrtcPart(audio[gameId], session);
webrtcPart(audio[gameId]!, session);
break;
case "relayICECandidate":

View File

@ -10,7 +10,13 @@
import { Session } from "./games/types";
export const audio: Record<string, any> = {};
interface Peer {
ws: any;
name: string;
}
/* Map of session => peer_id => peer */
export const audio: Record<string, Record<string, Peer>> = {};
// Default send helper used when caller doesn't provide a safeSend implementation.
const defaultSend = (targetOrSession: any, message: any): boolean => {
@ -29,7 +35,7 @@ const defaultSend = (targetOrSession: any, message: any): boolean => {
}
};
export const join = (peers: any, session: any): void => {
export const join = (peers: Record<string, Peer>, session: Session): void => {
const send = defaultSend;
const ws = session.ws;
@ -45,18 +51,19 @@ export const join = (peers: any, session: any): void => {
console.log(`${session.short}: <- join - ${session.name}`);
const peer = peers[session.id];
// Use session.id as the canonical peer key
if (session.id in peers) {
if (peer) {
console.log(`${session.short}:${session.id} - Already joined to Audio, updating WebSocket reference.`);
try {
const prev = peers[session.id] && peers[session.id].ws;
const prev = peer.ws;
if (prev && prev._pingInterval) {
clearInterval(prev._pingInterval);
}
} catch (e) {
/* ignore */
}
peers[session.id].ws = ws;
peer.ws = ws;
send(ws, {
type: "join_status",
@ -72,7 +79,7 @@ export const join = (peers: any, session: any): void => {
type: "addPeer",
data: {
peer_id: peerId,
peer_name: peers[peerId].name || peerId,
peer_name: peers[peerId]!.name,
should_create_offer: true,
},
});
@ -82,7 +89,7 @@ export const join = (peers: any, session: any): void => {
for (const peerId in peers) {
if (peerId === session.id) continue;
send(peers[peerId].ws, {
send(peers[peerId]!.ws, {
type: "addPeer",
data: {
peer_id: session.id,
@ -97,7 +104,7 @@ export const join = (peers: any, session: any): void => {
for (let peerId in peers) {
// notify existing peers about the new client
send(peers[peerId].ws, {
send(peers[peerId]!.ws, {
type: "addPeer",
data: {
peer_id: session.id,
@ -111,7 +118,7 @@ export const join = (peers: any, session: any): void => {
type: "addPeer",
data: {
peer_id: peerId,
peer_name: peers[peerId].name || peerId,
peer_name: peers[peerId]!.name || peerId,
should_create_offer: true,
},
});
@ -130,7 +137,7 @@ export const join = (peers: any, session: any): void => {
});
};
export const part = (peers: any, session: any): void => {
export const part = (peers: Record<string, Peer>, session: Session): void => {
const ws = session.ws;
const send = defaultSend;
@ -151,7 +158,7 @@ export const part = (peers: any, session: any): void => {
delete peers[session.id];
for (let peerId in peers) {
send(peers[peerId].ws, {
send(peers[peerId]!.ws, {
type: "removePeer",
data: {
peer_id: session.id,
@ -162,7 +169,7 @@ export const part = (peers: any, session: any): void => {
type: "removePeer",
data: {
peer_id: peerId,
peer_name: peers[peerId].name || peerId,
peer_name: peers[peerId]!.name || peerId,
},
});
}
@ -195,8 +202,8 @@ export const handleRelayICECandidate = (gameId: string, cfg: any, session: Sessi
},
});
if (peer_id in audio[gameId]) {
const target = audio[gameId][peer_id] as any;
if (peer_id in audio[gameId]!) {
const target = audio[gameId]![peer_id] as any;
if (!target || !target.ws) {
console.warn(`${session.id}:${gameId} relayICECandidate - target ${peer_id} has no ws`);
} else if (!send(target.ws, message)) {
@ -236,8 +243,8 @@ export const handleRelaySessionDescription = (gameId: string, cfg: any, session:
session_description,
},
});
if (peer_id in audio[gameId]) {
const target = audio[gameId][peer_id] as any;
if (peer_id in audio[gameId]!) {
const target = audio[gameId]![peer_id] as any;
if (!target || !target.ws) {
console.warn(`${session.id}:${gameId} relaySessionDescription - target ${peer_id} has no ws`);
} else if (!send(target.ws, message)) {