Audio is now working, although there are loading race conditions :(
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
f49351dcdf
commit
6cee42845d
@ -46,6 +46,7 @@ const Table = () => {
|
||||
const data = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case 'game-update':
|
||||
console.log(`ws: message - ${data.type}`, data.update);
|
||||
if ('name' in data.update && data.update.name !== name) {
|
||||
console.log(`Updating name to ${data.update.name}`);
|
||||
setName(data.update.name);
|
||||
|
@ -1,6 +1,6 @@
|
||||
.MediaAgent {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 50000;
|
||||
|
@ -278,6 +278,7 @@ const MediaAgent = () => {
|
||||
return;
|
||||
}
|
||||
let update = false;
|
||||
if (stream) {
|
||||
if (!(name in peers)) {
|
||||
update = true;
|
||||
peers[name] = {
|
||||
@ -286,6 +287,7 @@ const MediaAgent = () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in peers) {
|
||||
if (peers[key].local && key !== name) {
|
||||
@ -297,14 +299,14 @@ const MediaAgent = () => {
|
||||
console.log(`MediaAgent - Adding local`, peers);
|
||||
setPeers(Object.assign({}, peers));
|
||||
}
|
||||
}, [peers, name, setPeers]);
|
||||
}, [peers, name, setPeers, stream]);
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(async () => {
|
||||
if (!ws) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setup_local_media = async () => {
|
||||
const setup_local_media = () => {
|
||||
/* Ask user for permission to use the computers microphone and/or camera,
|
||||
* attach it to an <audio> or <video> tag if they give us access. */
|
||||
console.log("Requesting access to local audio / video inputs");
|
||||
@ -314,28 +316,25 @@ const MediaAgent = () => {
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia);
|
||||
|
||||
return await navigator.mediaDevices.getUserMedia({audio: true, video: false})//, "video": true})
|
||||
return navigator.mediaDevices.getUserMedia({audio: true, video: false})//, "video": true})
|
||||
.then((media) => { /* user accepted access to a/v */
|
||||
console.log("Access granted to audio/video");
|
||||
setStream(media);
|
||||
})
|
||||
.catch((error) => { /* user denied access to a/v */
|
||||
console.error(error);
|
||||
console.log("Access denied for audio/video");
|
||||
window.alert("You chose not to provide access to the microphone!" +
|
||||
"Ketran will have no audio :(");
|
||||
});
|
||||
};
|
||||
|
||||
const join_chat = () => {
|
||||
const join = () => {
|
||||
ws.send(JSON.stringify({ type: 'join' }));
|
||||
}
|
||||
|
||||
console.log(`MediaAgent - WebSocket open request. Attempting to create local media.`)
|
||||
setup_local_media().then(() => {
|
||||
await setup_local_media().then(() => {
|
||||
/* once the user has given us access to their
|
||||
* microphone/camcorder, join the channel and start peering up */
|
||||
join_chat();
|
||||
join();
|
||||
}).catch((error) => { /* user denied access to a/v */
|
||||
console.error(error);
|
||||
console.log("Access denied for audio/video");
|
||||
});
|
||||
}, [ws, setStream]);
|
||||
|
||||
@ -398,7 +397,6 @@ const MediaControl = ({isSelf, peer}) => {
|
||||
const [control, setControl] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(peer, peers);
|
||||
setControl(peers[peer]);
|
||||
}, [peer, peers, setControl]);
|
||||
|
||||
|
@ -3,8 +3,21 @@
|
||||
position: relative;
|
||||
padding: 0.5em;
|
||||
user-select: none;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.PlayerList .Unselected {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.PlayerList .Unselected > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
.PlayerList .PlayerSelector .PlayerColor {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
@ -10,6 +10,7 @@ import { GlobalContext } from "./GlobalContext.js";
|
||||
const PlayerList = () => {
|
||||
const { ws, name } = useContext(GlobalContext);
|
||||
const [players, setPlayers] = useState({});
|
||||
const [unselected, setUneslected] = useState([]);
|
||||
const [state, setState] = useState('lobby');
|
||||
const [color, setColor] = useState(undefined);
|
||||
|
||||
@ -17,7 +18,9 @@ const PlayerList = () => {
|
||||
const data = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case 'game-update':
|
||||
console.log(`PlayerList - onWsMessage`);
|
||||
if (data.update.unselected) {
|
||||
setUneslected(data.update.unselected);
|
||||
}
|
||||
if (data.update.players) {
|
||||
let found = false;
|
||||
for (let key in data.update.players) {
|
||||
@ -61,7 +64,7 @@ const PlayerList = () => {
|
||||
}
|
||||
ws.send(JSON.stringify({
|
||||
type: 'get',
|
||||
fields: [ 'state', 'players' ]
|
||||
fields: [ 'state', 'players', 'unselected' ]
|
||||
}));
|
||||
}, [ws]);
|
||||
|
||||
@ -94,11 +97,21 @@ const PlayerList = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const waiting = unselected.map((player) => {
|
||||
return <div key={player}>
|
||||
<div>{ player }</div>
|
||||
<MediaControl peer={player} isSelf={name === player}/>
|
||||
</div>
|
||||
})
|
||||
|
||||
return (
|
||||
<Paper className="PlayerList">
|
||||
<List className="PlayerSelector">
|
||||
{ playerElements }
|
||||
</List>
|
||||
<div className="Unselected">
|
||||
{ waiting }
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -559,15 +559,33 @@ const loadGame = async (id) => {
|
||||
game = createGame(id);
|
||||
}
|
||||
|
||||
/* Clear out cached names from player colors and rebuild them
|
||||
* from the information in the saved game sessions */
|
||||
for (let color in game.players) {
|
||||
delete game.players[color].name;
|
||||
game.players[color].status = 'Not active';
|
||||
}
|
||||
|
||||
/* Reconnect session player colors to the player objects */
|
||||
game.unselected = [];
|
||||
for (let id in game.sessions) {
|
||||
const session = game.sessions[id];
|
||||
if (session.color && session.color in game.players) {
|
||||
if (session.name && session.color && session.color in game.players) {
|
||||
session.player = game.players[session.color];
|
||||
session.player.name = session.name;
|
||||
session.player.status = 'Active';
|
||||
session.player.live = false;
|
||||
} else {
|
||||
session.color = undefined;
|
||||
session.player = undefined;
|
||||
}
|
||||
|
||||
session.live = false;
|
||||
|
||||
/* Populate the 'unselected' list from the session table */
|
||||
if (!game.sessions[id].color && game.sessions[id].name) {
|
||||
game.unselected.push(game.sessions[id]);
|
||||
}
|
||||
}
|
||||
|
||||
games[id] = game;
|
||||
@ -799,13 +817,22 @@ const setPlayerName = (game, session, name) => {
|
||||
const id = game.id;
|
||||
|
||||
let rejoin = false;
|
||||
|
||||
if (!name) {
|
||||
return `You can not set your name to nothing!`;
|
||||
}
|
||||
|
||||
if (name.toLowerCase() === 'the bank') {
|
||||
return `You cannot play as the bank!`;
|
||||
}
|
||||
|
||||
/* Check to ensure name is not already in use */
|
||||
if (game && name) for (let key in game.sessions) {
|
||||
for (let key in game.sessions) {
|
||||
const tmp = game.sessions[key];
|
||||
if (tmp === session) {
|
||||
if (tmp === session || !tmp.name) {
|
||||
continue;
|
||||
}
|
||||
if (tmp.name && tmp.name.toLowerCase() === name.toLowerCase()) {
|
||||
if (tmp.name.toLowerCase() === name.toLowerCase()) {
|
||||
if (!tmp.player || (Date.now() - tmp.player.lastActive) > 60000) {
|
||||
rejoin = true;
|
||||
/* Update the session object from tmp, but retain websocket
|
||||
@ -818,22 +845,11 @@ const setPlayerName = (game, session, name) => {
|
||||
return `${name} is already taken and has been active in the last minute.`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`Attempting to create new player for ${name}`);
|
||||
}
|
||||
|
||||
if (name.toLowerCase() === 'the bank') {
|
||||
return `You cannot play as the bank!`;
|
||||
}
|
||||
|
||||
const old = session.name;
|
||||
let message;
|
||||
|
||||
if (!name) {
|
||||
return `You can not set your name to nothing!`;
|
||||
}
|
||||
|
||||
if (!old) {
|
||||
if (!session.name) {
|
||||
message = `A new player has entered the lobby as ${name}.`;
|
||||
session.name = name;
|
||||
} else {
|
||||
@ -844,7 +860,7 @@ const setPlayerName = (game, session, name) => {
|
||||
part(audio[id], session, game.id);
|
||||
}
|
||||
} else {
|
||||
message = `${old} has changed their name to ${name}.`;
|
||||
message = `${session.name} has changed their name to ${name}.`;
|
||||
if (session.ws && id in audio) {
|
||||
part(audio[id], session, game.id);
|
||||
}
|
||||
@ -852,10 +868,15 @@ const setPlayerName = (game, session, name) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!session.color) {
|
||||
console.log(`Adding ${session.name} to the unselected`);
|
||||
game.unselected.push(session);
|
||||
}
|
||||
|
||||
if (session.ws) {
|
||||
join(audio[id], session, game.id);
|
||||
}
|
||||
|
||||
console.log(message);
|
||||
addChatMessage(game, null, message);
|
||||
return undefined;
|
||||
}
|
||||
@ -883,6 +904,16 @@ const setPlayerColor = (game, session, color) => {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify the player has a name set */
|
||||
if (!name) {
|
||||
return `You may only select a player when you have set your name.`;
|
||||
}
|
||||
|
||||
/* Verify selection is valid */
|
||||
if (!(color in game.players)) {
|
||||
return `An invalid player selection was attempted.`;
|
||||
}
|
||||
|
||||
const priorActive = getActiveCount(game);
|
||||
let message;
|
||||
|
||||
@ -899,24 +930,15 @@ const setPlayerColor = (game, session, color) => {
|
||||
session.color = undefined;
|
||||
}
|
||||
|
||||
/* Verify the player has a name set */
|
||||
if (!name) {
|
||||
return `You may only select a player when you have set your name.`;
|
||||
}
|
||||
|
||||
/* If the player is not selecting a color, then return */
|
||||
if (!color) {
|
||||
if (message) {
|
||||
console.log(message);
|
||||
addChatMessage(game, null, message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify selection is valid */
|
||||
if (!(color in game.players)) {
|
||||
return `An invalid player selection was attempted.`;
|
||||
}
|
||||
|
||||
/* Verify selection is not already taken */
|
||||
for (let key in game.sessions) {
|
||||
const tmp = game.sessions[key].player;
|
||||
@ -927,12 +949,19 @@ const setPlayerColor = (game, session, color) => {
|
||||
|
||||
/* All good -- set this player to requested selection */
|
||||
session.player = getPlayer(game, color);
|
||||
|
||||
session.player.name = name;
|
||||
session.player.status = `Active`;
|
||||
session.player.lastActive = Date.now();
|
||||
session.color = color;
|
||||
game.players[color].name = session.name;
|
||||
|
||||
/* Rebuild the unselected list */
|
||||
game.unselected = [];
|
||||
for (let id in game.sessions) {
|
||||
if (!game.sessions.color[id] && game.sessions[id].name) {
|
||||
game.unselected.push(game.sessions[id]);
|
||||
}
|
||||
}
|
||||
addChatMessage(game, session, `${session.name} has chosen to play as ${colorToWord(color)}.`);
|
||||
|
||||
const afterActive = getActiveCount(game);
|
||||
@ -2843,6 +2872,8 @@ const saveGame = async (game) => {
|
||||
reducedSessions.push(reduced);
|
||||
}
|
||||
|
||||
delete reducedGame.unselected;
|
||||
|
||||
/* Save per turn while debugging... */
|
||||
await writeFile(`games/${game.id}.${game.turns}`, JSON.stringify(reducedGame, null, 2))
|
||||
.catch((error) => {
|
||||
@ -2856,6 +2887,39 @@ const saveGame = async (game) => {
|
||||
});
|
||||
}
|
||||
|
||||
const departLobby = (game, session) => {
|
||||
const update = {};
|
||||
update.unselected = getFilteredUnselected(game);
|
||||
|
||||
if (session.player) {
|
||||
session.player.live = true;
|
||||
update.players = game.players;
|
||||
}
|
||||
|
||||
if (session.name) {
|
||||
addChatMessage(game, null, `${session.name} has left the lobby.`);
|
||||
update.chat = game.chat;
|
||||
}
|
||||
|
||||
const message = JSON.stringify({
|
||||
type: 'game-update',
|
||||
update
|
||||
});
|
||||
for (let key in game.sessions) {
|
||||
const _session = game.sessions[key];
|
||||
if (!_session.ws) {
|
||||
continue;
|
||||
}
|
||||
_session.ws.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
const getFilteredUnselected = (game) => {
|
||||
return game.unselected
|
||||
.filter(session => session.live)
|
||||
.map(session => session.name);
|
||||
}
|
||||
|
||||
router.ws("/ws/:id", async (ws, req) => {
|
||||
const { id } = req.params;
|
||||
const gameId = id;
|
||||
@ -2874,13 +2938,17 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
ws.on('error', async (event) => {
|
||||
console.error(`WebSocket error: `, event.message);
|
||||
const game = await loadGame(gameId);
|
||||
if (game) {
|
||||
if (!game) {
|
||||
return;
|
||||
}
|
||||
const session = getSession(game, req.session);
|
||||
if (session && session.ws) {
|
||||
session.live = false;
|
||||
if (session.ws) {
|
||||
session.ws.close();
|
||||
session.ws = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
departLobby(game, session);
|
||||
});
|
||||
|
||||
ws.on('open', async (event) => {
|
||||
@ -2893,9 +2961,15 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
|
||||
ws.on('close', async (event) => {
|
||||
const game = await loadGame(gameId);
|
||||
if (game) {
|
||||
if (!game) {
|
||||
return;
|
||||
}
|
||||
const session = getSession(game, req.session);
|
||||
if (session && session.ws) {
|
||||
if (session.player) {
|
||||
session.player.live = false;
|
||||
}
|
||||
session.live = false;
|
||||
if (session.ws) {
|
||||
/* Cleanup any voice channels */
|
||||
if (id in audio) {
|
||||
part(audio[id], session, id);
|
||||
@ -2904,7 +2978,8 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
session.ws = undefined;
|
||||
console.log(`WebSocket closed for ${getName(session)}`);
|
||||
}
|
||||
}
|
||||
|
||||
departLobby(game, session);
|
||||
|
||||
console.log(`${id}:${ws.id} - closed connection`);
|
||||
});
|
||||
@ -2923,14 +2998,14 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
return;
|
||||
}
|
||||
const session = getSession(game, req.session);
|
||||
if (!session) {
|
||||
console.error(`Unable to obtain session.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session.ws) {
|
||||
session.ws = ws;
|
||||
}
|
||||
if (session.player) {
|
||||
session.player.live = true;
|
||||
}
|
||||
session.live = true;
|
||||
session.lastActive = Date.now();
|
||||
|
||||
let error = '', update;
|
||||
|
||||
@ -2997,18 +3072,9 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
console.log(`${id}:${getName(session)} - setPlayerName - ${data.name}`)
|
||||
error = setPlayerName(game, session, data.name);
|
||||
if (error) {
|
||||
session.ws.send(JSON.stringify({ error }));
|
||||
session.ws.send(JSON.stringify({ type: 'error', error }));
|
||||
break;
|
||||
}
|
||||
update = {};
|
||||
|
||||
session.name = data.name;
|
||||
update.name = session.name
|
||||
if (session.color && session.color in game.players) {
|
||||
game.players[session.color].name = session.name;
|
||||
update.players = game.players;
|
||||
}
|
||||
|
||||
for (let key in game.sessions) {
|
||||
const _session = game.sessions[key];
|
||||
if (!_session.ws) {
|
||||
@ -3024,13 +3090,13 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
break;
|
||||
|
||||
case 'set':
|
||||
console.log(`${id}:${getName(session)} - ${data.type} = ${data.value}`);
|
||||
console.log(`${id}:${getName(session)} - ${data.type} ${data.field} = ${data.value}`);
|
||||
update = {};
|
||||
switch (data.field) {
|
||||
case 'color':
|
||||
error = setPlayerColor(game, session, data.value);
|
||||
if (error) {
|
||||
session.ws.send(JSON.stringify({ error }));
|
||||
session.ws.send(JSON.stringify({ type: error, error }));
|
||||
break;
|
||||
}
|
||||
for (let key in game.sessions) {
|
||||
@ -3053,7 +3119,7 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
console.log(`${id}:${getName(session)} - ${data.type}`);
|
||||
console.log(`${id}:${getName(session)} - ${data.type} ${data.fields.join(',')}`);
|
||||
update = {};
|
||||
data.fields.forEach((field) => {
|
||||
switch (field) {
|
||||
@ -3062,6 +3128,9 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
case 'state':
|
||||
update[field] = game[field];
|
||||
break;
|
||||
case 'unselected':
|
||||
update[field] = getFilteredUnselected(game);
|
||||
break;
|
||||
case 'players':
|
||||
update[field] = game[field];
|
||||
for (let color in game.players) {
|
||||
@ -3123,10 +3192,7 @@ router.ws("/ws/:id", async (ws, req) => {
|
||||
}
|
||||
|
||||
const session = getSession(game, req.session);
|
||||
if (!session) {
|
||||
console.error(`Session should never be empty after getSession`,
|
||||
game, req.session);
|
||||
}
|
||||
session.lastActive = Date.now();
|
||||
|
||||
resetDisconnectCheck(game, req);
|
||||
|
||||
@ -3449,6 +3515,8 @@ const filterGameForPlayer = (game, session) => {
|
||||
/* Strip out data that should not be shared with players */
|
||||
delete reducedGame.developmentCards;
|
||||
|
||||
reducedGame.unselected = getFilteredUnselected(game);
|
||||
|
||||
return Object.assign(reducedGame, {
|
||||
timestamp: Date.now(),
|
||||
status: session.error ? session.error : "success",
|
||||
|
Loading…
x
Reference in New Issue
Block a user