Started refactoring during voice enabling
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
5499a66fd3
commit
4909647e75
@ -11,7 +11,7 @@ import TextField from '@material-ui/core/TextField';
|
|||||||
import Resource from './Resource.js';
|
import Resource from './Resource.js';
|
||||||
import Dice from './Dice.js';
|
import Dice from './Dice.js';
|
||||||
|
|
||||||
const Chat = ({ table }) => {
|
const Chat = ({ table, game }) => {
|
||||||
const [lastTop, setLastTop] = useState(0),
|
const [lastTop, setLastTop] = useState(0),
|
||||||
[autoScroll, setAutoscroll] = useState(true),
|
[autoScroll, setAutoscroll] = useState(true),
|
||||||
[latest, setLatest] = useState(''),
|
[latest, setLatest] = useState(''),
|
||||||
@ -76,11 +76,11 @@ const Chat = ({ table }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//const timeDelta = game.timestamp - Date.now();
|
//const timeDelta = game.timestamp - Date.now();
|
||||||
if (!table.game) {
|
if (!game.id) {
|
||||||
console.log("Why no game?");
|
console.log("Why no game id?");
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = table.game && table.game.chat.map((item, index) => {
|
const messages = game && game.chat.map((item, index) => {
|
||||||
const punctuation = item.message.match(/(\.+$)/);
|
const punctuation = item.message.match(/(\.+$)/);
|
||||||
let period;
|
let period;
|
||||||
if (punctuation) {
|
if (punctuation) {
|
||||||
@ -130,15 +130,15 @@ const Chat = ({ table }) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (table.game && table.game.chat &&
|
if (game.chat &&
|
||||||
table.game.chat.length &&
|
game.chat.length &&
|
||||||
table.game.chat[table.game.chat.length - 1].date !== latest) {
|
game.chat[game.chat.length - 1].date !== latest) {
|
||||||
setLatest(table.game.chat[table.game.chat.length - 1].date);
|
setLatest(game.chat[game.chat.length - 1].date);
|
||||||
setAutoscroll(true);
|
setAutoscroll(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = table.game ? table.game.name : "Why no game?";
|
const name = game ? game.name : "Why no game?";
|
||||||
const elapsed = table.game ? (table.game.timestamp - table.game.startTime) : undefined;
|
const elapsed = game ? (game.timestamp - game.startTime) : undefined;
|
||||||
return (
|
return (
|
||||||
<Paper className="Chat">
|
<Paper className="Chat">
|
||||||
<List className="ChatList" id="ChatList" onScroll={chatScroll}>
|
<List className="ChatList" id="ChatList" onScroll={chatScroll}>
|
||||||
@ -148,7 +148,7 @@ const Chat = ({ table }) => {
|
|||||||
disabled={!name}
|
disabled={!name}
|
||||||
onChange={chatInput}
|
onChange={chatInput}
|
||||||
onKeyPress={chatKeyPress}
|
onKeyPress={chatKeyPress}
|
||||||
label={elapsed && <Moment tz={"Etc/GMT"} format="h:mm:ss" durationFromNow interval={1000} date={table.game.startTime}></Moment>} variant="outlined"/>
|
label={elapsed && <Moment tz={"Etc/GMT"} format="h:mm:ss" durationFromNow interval={1000} date={game.startTime}></Moment>} variant="outlined"/>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useRef, useCallback,
|
||||||
|
useContext, createContext } from "react";
|
||||||
import "./MediaControl.css";
|
import "./MediaControl.css";
|
||||||
|
|
||||||
import VolumeOff from '@mui/icons-material/VolumeOff';
|
import VolumeOff from '@mui/icons-material/VolumeOff';
|
||||||
@ -6,53 +7,169 @@ import VolumeUp from '@mui/icons-material/VolumeUp';
|
|||||||
import MicOff from '@mui/icons-material/MicOff';
|
import MicOff from '@mui/icons-material/MicOff';
|
||||||
import Mic from '@mui/icons-material/Mic';
|
import Mic from '@mui/icons-material/Mic';
|
||||||
|
|
||||||
const USE_AUDIO = true;
|
const MediaContext = createContext();
|
||||||
const DEFAULT_CHANNEL = 'some-global-channel-name';
|
|
||||||
const MUTE_AUDIO_BY_DEFAULT = false;
|
|
||||||
let local_media_stream = undefined;
|
|
||||||
|
|
||||||
const ICE_SERVERS = [
|
/* Proxy object so we can pass in srcObject to <audio> */
|
||||||
{urls:"stun:stun.l.google.com:19302"}
|
const Audio = ({ srcObject, ...props }) => {
|
||||||
];
|
const refAudio = useRef(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!refAudio.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('<audio> bind');
|
||||||
|
refAudio.current.srcObject = srcObject;
|
||||||
|
return () => {
|
||||||
|
console.log('<audio> unbind');
|
||||||
|
refAudio.current.srcObject = undefined;
|
||||||
|
};
|
||||||
|
}, [srcObject]);
|
||||||
|
return <audio ref={refAudio} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
const MediaAgent = ({ game, ws }) => {
|
const MediaAgent = ({ name, ws }) => {
|
||||||
const [peers, setPeers] = useState({});
|
const [stream, setStream] = useState(undefined);
|
||||||
|
const { peers, setPeers } = useContext(MediaContext);
|
||||||
|
|
||||||
if (!game || !ws) {
|
const onClose = useCallback((event) => {
|
||||||
|
console.log(`${name} Disconnected from signaling server`);
|
||||||
|
/* Tear down all of our peer connections and remove all the
|
||||||
|
* media divs when we disconnect */
|
||||||
|
for (let peer_id in peers) {
|
||||||
|
peers[peer_id].close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let id in peers) {
|
||||||
|
delete peers[id];
|
||||||
|
}
|
||||||
|
setPeers(Object.assign({}, peers));
|
||||||
|
}, [peers, setPeers, name]);
|
||||||
|
|
||||||
|
const onOpen = (event) => {
|
||||||
|
console.log(`MediaAgent - WebSocket open request. Attempting to create local media.`)
|
||||||
|
setup_local_media().then(() => {
|
||||||
|
/* once the user has given us access to their
|
||||||
|
* microphone/camcorder, join the channel and start peering up */
|
||||||
|
join_chat(ws);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
switch (data.type) {
|
||||||
|
case 'addPeer': addPeer(data.data); break;
|
||||||
|
case 'removePeer': removePeer(data.data); break;
|
||||||
|
case 'iceCandidate': iceCandidate(data.data); break;
|
||||||
|
case 'sessionDescription': sessionDescription(data.data); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onError = (event) => {
|
||||||
|
console.error(`${name} WebSocket error`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setup_local_media = useCallback(async () => {
|
||||||
|
if (stream !== undefined) { /* ie, if we've already been initialized */
|
||||||
|
console.log(stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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");
|
||||||
|
|
||||||
|
navigator.getUserMedia = (navigator.getUserMedia ||
|
||||||
|
navigator.webkitGetUserMedia ||
|
||||||
|
navigator.mozGetUserMedia ||
|
||||||
|
navigator.msGetUserMedia);
|
||||||
|
|
||||||
|
return await 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 :(");
|
||||||
|
});
|
||||||
|
}, [ setStream, stream ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ws) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('MediaAgent bind');
|
||||||
|
|
||||||
|
console.log('Media Agent peers: ', peers);
|
||||||
|
|
||||||
|
ws.addEventListener('message', onMessage);
|
||||||
|
ws.addEventListener('error', onError);
|
||||||
|
ws.addEventListener('close', onClose);
|
||||||
|
ws.addEventListener('open', onOpen);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
console.log('MediaAgent unbind');
|
||||||
|
|
||||||
|
ws.removeEventListener('message', onMessage);
|
||||||
|
ws.removeEventListener('error', onError);
|
||||||
|
ws.removeEventListener('close', onClose);
|
||||||
|
ws.removeEventListener('open', onOpen);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`MediaAgent`);
|
||||||
const addPeer = (config) => {
|
const addPeer = (config) => {
|
||||||
console.log('Signaling server said to add peer:', config);
|
console.log('Signaling server said to add peer:', config);
|
||||||
const peer_id = config.peer_id;
|
|
||||||
if (peer_id in peers) {
|
if (!stream) {
|
||||||
/* This could happen if the user joins multiple channels where the other peer is also in. */
|
console.log(`No local media stream`);
|
||||||
console.log("Already connected to peer ", peer_id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const peer_connection = new RTCPeerConnection(
|
|
||||||
{ iceServers: ICE_SERVERS },
|
const peer_id = config.peer_id;
|
||||||
|
if (peer_id in peers) {
|
||||||
|
/* This could happen if the user joins multiple channels where the
|
||||||
|
* other peer is also in. */
|
||||||
|
//console.log("Already connected to peer ", peer_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const peer_connection = new RTCPeerConnection({
|
||||||
|
/*configuration: {
|
||||||
|
offerToReceiveAudio: true,
|
||||||
|
offerToReceiveVideo: false
|
||||||
|
},*/
|
||||||
|
iceServers: [ { urls: "stun:stun.l.google.com:19302" } ]
|
||||||
|
}, {
|
||||||
/* this will no longer be needed by chrome
|
/* this will no longer be needed by chrome
|
||||||
* eventually (supposedly), but is necessary
|
* eventually (supposedly), but is necessary
|
||||||
* for now to get firefox to talk to chrome */
|
* for now to get firefox to talk to chrome */
|
||||||
{ optional: [{DtlsSrtpKeyAgreement: true}]}
|
optional: [{DtlsSrtpKeyAgreement: true}]
|
||||||
);
|
});
|
||||||
|
|
||||||
peers[peer_id] = peer_connection;
|
peers[peer_id] = peer_connection;
|
||||||
setPeers(Object.assign({}, peers));
|
setPeers(Object.assign({}, peers));
|
||||||
|
|
||||||
peer_connection.onicecandidate = (event) => {
|
peer_connection.onicecandidate = (event) => {
|
||||||
if (event.candidate) {
|
if (!event.candidate) {
|
||||||
ws.send(JSON.stringify({
|
return;
|
||||||
type: 'relayICECandidate',
|
|
||||||
config: {
|
|
||||||
peer_id: peer_id,
|
|
||||||
ice_candidate: {
|
|
||||||
sdpMLineIndex: event.candidate.sdpMLineIndex,
|
|
||||||
candidate: event.candidate.candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'relayICECandidate',
|
||||||
|
config: {
|
||||||
|
peer_id: peer_id,
|
||||||
|
ice_candidate: {
|
||||||
|
sdpMLineIndex: event.candidate.sdpMLineIndex,
|
||||||
|
candidate: event.candidate.candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
peer_connection.ontrack = (event) => {
|
peer_connection.ontrack = (event) => {
|
||||||
@ -62,30 +179,32 @@ const MediaAgent = ({ game, ws }) => {
|
|||||||
muted: true,
|
muted: true,
|
||||||
controls: true
|
controls: true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window.URL) {
|
if (window.URL) {
|
||||||
peer_connection.extra = {
|
peer_connection.attributes.srcObject = event.streams[0];
|
||||||
srcObject: event.streams[0]
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
peer_connection.extra = {
|
peer_connection.attributes.src = event.streams[0];
|
||||||
src: event.streams[0]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPeers(Object.assign({}, peers));
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Add our local stream */
|
/* Add our local stream */
|
||||||
peer_connection.addStream(local_media_stream);
|
peer_connection.addStream(stream);
|
||||||
|
|
||||||
/* Only one side of the peer connection should create the
|
/* Only one side of the peer connection should create the
|
||||||
* offer, the signaling server picks one to be the offerer.
|
* offer, the signaling server picks one to be the offerer.
|
||||||
* The other user will get a 'sessionDescription' event and will
|
* The other user will get a 'sessionDescription' event and will
|
||||||
* create an offer, then send back an answer 'sessionDescription' to us
|
* create an offer, then send back an answer 'sessionDescription'
|
||||||
|
* to us
|
||||||
*/
|
*/
|
||||||
if (config.should_create_offer) {
|
if (config.should_create_offer) {
|
||||||
console.log("Creating RTC offer to ", peer_id);
|
console.log("MediaAgent - Creating RTC offer to ", peer_id);
|
||||||
peer_connection.createOffer((local_description) => {
|
return peer_connection.createOffer()
|
||||||
|
.then((local_description) => {
|
||||||
console.log("Local offer description is: ", local_description);
|
console.log("Local offer description is: ", local_description);
|
||||||
peer_connection.setLocalDescription(local_description, () => {
|
return peer_connection.setLocalDescription(local_description)
|
||||||
|
.then(() => {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'relaySessionDescription',
|
type: 'relaySessionDescription',
|
||||||
config: {
|
config: {
|
||||||
@ -94,10 +213,12 @@ const MediaAgent = ({ game, ws }) => {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
console.log("Offer setLocalDescription succeeded");
|
console.log("Offer setLocalDescription succeeded");
|
||||||
}, () => {
|
|
||||||
console.error("Offer setLocalDescription failed!");
|
|
||||||
})
|
})
|
||||||
}, (error) => {
|
.catch((error) => {
|
||||||
|
console.error("Offer setLocalDescription failed!");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
console.log("Error sending offer: ", error);
|
console.log("Error sending offer: ", error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -110,14 +231,18 @@ const MediaAgent = ({ game, ws }) => {
|
|||||||
* the 'offerer' sends a description to the 'answerer' (with type
|
* the 'offerer' sends a description to the 'answerer' (with type
|
||||||
* "offer"), then the answerer sends one back (with type "answer").
|
* "offer"), then the answerer sends one back (with type "answer").
|
||||||
*/
|
*/
|
||||||
console.log('Remote description received: ', peer_id, session_description);
|
// console.log('Remote description received: ', peer_id, session_description);
|
||||||
const peer = peers[peer_id];
|
const peer = peers[peer_id];
|
||||||
console.log(session_description);
|
if (!peer) {
|
||||||
|
console.error(`No peer for ${peer_id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.log(session_description);
|
||||||
|
|
||||||
const desc = new RTCSessionDescription(session_description);
|
const desc = new RTCSessionDescription(session_description);
|
||||||
const stuff = peer.setRemoteDescription(desc, () => {
|
return peer.setRemoteDescription(desc, () => {
|
||||||
console.log("setRemoteDescription succeeded");
|
console.log("setRemoteDescription succeeded");
|
||||||
if (session_description.type == "offer") {
|
if (session_description.type === "offer") {
|
||||||
console.log("Creating answer");
|
console.log("Creating answer");
|
||||||
peer.createAnswer((local_description) => {
|
peer.createAnswer((local_description) => {
|
||||||
console.log("Answer description is: ", local_description);
|
console.log("Answer description is: ", local_description);
|
||||||
@ -134,15 +259,15 @@ const MediaAgent = ({ game, ws }) => {
|
|||||||
console.error("Answer setLocalDescription failed!");
|
console.error("Answer setLocalDescription failed!");
|
||||||
});
|
});
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.log("Error creating answer: ", error);
|
// console.log("Error creating answer: ", error);
|
||||||
console.log(peer);
|
console.error(peer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.log("setRemoteDescription error: ", error);
|
console.log("setRemoteDescription error: ", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Description Object: ", desc);
|
// console.log("Description Object: ", desc);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removePeer = ({peer_id}) => {
|
const removePeer = ({peer_id}) => {
|
||||||
@ -155,185 +280,92 @@ const MediaAgent = ({ game, ws }) => {
|
|||||||
setPeers(Object.assign({}, peers));
|
setPeers(Object.assign({}, peers));
|
||||||
};
|
};
|
||||||
|
|
||||||
const iceCandidate = (config) => {
|
const iceCandidate = ({ peer_id, ice_candidate }) => {
|
||||||
/**
|
/**
|
||||||
* The offerer will send a number of ICE Candidate blobs to the answerer so they
|
* The offerer will send a number of ICE Candidate blobs to the
|
||||||
* can begin trying to find the best path to one another on the net.
|
* answerer so they can begin trying to find the best path to one
|
||||||
|
* another on the net.
|
||||||
*/
|
*/
|
||||||
const peer = peers[config.peer_id],
|
const peer = peers[peer_id];
|
||||||
ice_candidate = config.ice_candidate;
|
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
console.error(`No peer for ${config.peer_id}`);
|
console.error(`No peer for ${peer_id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
peer.addIceCandidate(new RTCIceCandidate(ice_candidate))
|
peer.addIceCandidate(new RTCIceCandidate(ice_candidate))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
console.log(`Successfully added Ice Candidate for ${peer_id}`);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(peer, ice_candidate);
|
console.error(error, peer, ice_candidate);
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.addEventListener('message', (event) => {
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = JSON.parse(event.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (data.type) {
|
|
||||||
case 'addPeer': addPeer(data.data); break;
|
|
||||||
case 'removePeer': removePeer(data.data); break;
|
|
||||||
case 'iceCandidate': iceCandidate(data.data); break;
|
|
||||||
case 'sessionDescription': sessionDescription(data.data); break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.addEventListener('error', (event) => {
|
|
||||||
console.error(`${game.name} WebSocket error`);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.addEventListener('close', (event) => {
|
|
||||||
console.log(`${game.name} Disconnected from signaling server`);
|
|
||||||
/* Tear down all of our peer connections and remove all the
|
|
||||||
* media divs when we disconnect */
|
|
||||||
for (let peer_id in peers) {
|
|
||||||
peers[peer_id].close();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let id in peers) {
|
|
||||||
delete peers[id];
|
|
||||||
}
|
|
||||||
setPeers(Object.assign({}, peers));
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.addEventListener('open', (event) => {
|
|
||||||
setup_local_media(() => {
|
|
||||||
/* once the user has given us access to their
|
|
||||||
* microphone/camcorder, join the channel and start peering up */
|
|
||||||
join_chat_channel(ws, game.id, {'whatever-you-want-here': 'stuff'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const setup_local_media = (callback, errorback) => {
|
|
||||||
if (local_media_stream !== undefined) { /* ie, if we've already been initialized */
|
|
||||||
if (callback) callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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");
|
|
||||||
|
|
||||||
navigator.getUserMedia = (navigator.getUserMedia ||
|
|
||||||
navigator.webkitGetUserMedia ||
|
|
||||||
navigator.mozGetUserMedia ||
|
|
||||||
navigator.msGetUserMedia);
|
|
||||||
|
|
||||||
navigator.mediaDevices.getUserMedia({audio: true, video: false})//, "video": true})
|
|
||||||
.then((stream) => { /* user accepted access to a/v */
|
|
||||||
console.log("Access granted to audio/video");
|
|
||||||
|
|
||||||
local_media_stream = stream;
|
|
||||||
var local_media = document.querySelector('audio.local');
|
|
||||||
if (!local_media) {
|
|
||||||
console.error(`No local media!`);
|
|
||||||
}
|
|
||||||
if (window.URL) {
|
|
||||||
local_media.srcobject = stream;
|
|
||||||
} else {
|
|
||||||
local_media.src = stream;
|
|
||||||
}
|
|
||||||
if (callback) callback();
|
|
||||||
})
|
|
||||||
.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 :(");
|
|
||||||
if (errorback) errorback();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const join_chat_channel = (ws, channel, userdata) => {
|
const join_chat = (ws) => {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({ type: 'join' }));
|
||||||
type: 'join',
|
|
||||||
config: { channel, userdata }
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
const part_chat_channel = (channel) => {
|
const part_chat_channel = (channel) => {
|
||||||
ws.send(JSON.stringify({ type: 'part', config: channel }));
|
ws.send(JSON.stringify({ type: 'part' }));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
const audioPeers = [];
|
const audioPeers = [];
|
||||||
for (let id in peers) {
|
for (let id in peers) {
|
||||||
const peer = peers[id];
|
const peer = peers[id];
|
||||||
if (peer.attributes) {
|
if (peer.attributes) {
|
||||||
audioPeers.push(<audio
|
audioPeers.push(
|
||||||
ref={el => {
|
<Audio
|
||||||
if (!el) {
|
key={`Peer-${id}`}
|
||||||
return;
|
className="Remote"
|
||||||
}
|
{...peer.attributes}/>
|
||||||
|
);
|
||||||
if (peer.extra.srcObject) {
|
|
||||||
el.srcObject = peer.extra.srcObject;
|
|
||||||
} else {
|
|
||||||
el.src = peer.extra.src;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
key={`Peer-${id}`} className="Remote" {...peer.attributes}/>);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="MediaAgent">
|
return <div className="MediaAgent">
|
||||||
<audio className="local" autoPlay="autoplay" muted={true} controls/>
|
<Audio className="local" srcObject={stream} autoPlay="autoplay" muted={true} controls/>
|
||||||
{ audioPeers }
|
{ audioPeers }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MediaControl = ({game, color }) => {
|
const MediaControl = ({isSelf, peer}) => {
|
||||||
const [mute, setMute] = useState(false);
|
const [mute, setMute] = useState(false);
|
||||||
const [mic, setMic] = useState(true);
|
const [mic, setMic] = useState(true);
|
||||||
const [noAudio, setNoAudio] = useState(false);
|
const [noAudio, setNoAudio] = useState(false);
|
||||||
const [rtc, setRtc] = useState(undefined);
|
const peers = useContext(MediaContext);
|
||||||
|
/*
|
||||||
if (!game) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.players[color].status !== 'Active') {
|
if (game.players[color].status !== 'Active') {
|
||||||
return <div className="MediaControl"/>;
|
return <div className="MediaControl"/>;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
const isSelf = game.color === color;
|
|
||||||
|
|
||||||
if (!rtc) {
|
|
||||||
// setRtc(RTC({ id: game.id }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleMic = (event) => {
|
const toggleMic = (event) => {
|
||||||
|
if (peer in peers) {
|
||||||
|
console.log('found color in peers!');
|
||||||
|
}
|
||||||
setMic(!mic);
|
setMic(!mic);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleMute = (event) => {
|
const toggleMute = (event) => {
|
||||||
|
if (peer in peers) {
|
||||||
|
console.log('found color in peers!');
|
||||||
|
}
|
||||||
setMute(!mute);
|
setMute(!mute);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<div className="MediaControl">
|
return <MediaContext.Provider value={peers}>
|
||||||
{ isSelf && <div onClick={toggleMic}>
|
<div className="MediaControl">
|
||||||
{ mic && <MicOff color={noAudio ? 'disabled' : 'primary'}/> }
|
{ isSelf && <div onClick={toggleMic}>
|
||||||
{ !mic && <Mic color={noAudio ? 'disabled' : 'primary'}/> }
|
{ mic && <MicOff color={noAudio ? 'disabled' : 'primary'}/> }
|
||||||
</div> }
|
{ !mic && <Mic color={noAudio ? 'disabled' : 'primary'}/> }
|
||||||
<div onClick={toggleMute}>
|
</div> }
|
||||||
{ mute && <VolumeOff color={noAudio ? 'disabled' : 'primary'}/> }
|
<div onClick={toggleMute}>
|
||||||
{ !mute && <VolumeUp color={noAudio ? 'disabled' : 'primary'}/> }
|
{ mute && <VolumeOff color={noAudio ? 'disabled' : 'primary'}/> }
|
||||||
|
{ !mute && <VolumeUp color={noAudio ? 'disabled' : 'primary'}/> }
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</MediaContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { MediaControl, MediaAgent };
|
export { MediaControl, MediaAgent, MediaContext };
|
||||||
|
@ -294,6 +294,11 @@
|
|||||||
background-color: rgba(255, 255, 0, 0.5);
|
background-color: rgba(255, 255, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Players .PlayerEntry > *:last-child {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.Players .PlayerToggle {
|
.Players .PlayerToggle {
|
||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
|
@ -19,7 +19,7 @@ import 'moment-timezone';
|
|||||||
import Activities from './Activities.js';
|
import Activities from './Activities.js';
|
||||||
import Placard from './Placard.js';
|
import Placard from './Placard.js';
|
||||||
import PlayersStatus from './PlayersStatus.js';
|
import PlayersStatus from './PlayersStatus.js';
|
||||||
import { MediaAgent, MediaControl } from './MediaControl.js';
|
import { MediaAgent, MediaControl, MediaContext } from './MediaControl.js';
|
||||||
import { base, assetsPath, getPlayerName, gamesPath } from './Common.js';
|
import { base, assetsPath, getPlayerName, gamesPath } from './Common.js';
|
||||||
|
|
||||||
/* Start of withRouter polyfill */
|
/* Start of withRouter polyfill */
|
||||||
@ -57,39 +57,37 @@ const Development = ({table, type, card, onClick}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StartButton = ({ table }) => {
|
const StartButton = ({ table, game }) => {
|
||||||
const startClick = (event) => {
|
const startClick = (event) => {
|
||||||
table.setGameState("game-order").then((state) => {
|
table.setGameState("game-order");
|
||||||
table.game.state = state;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button disabled={!table.game.color || table.game.active < 2} onClick={startClick}>Start game</Button>
|
<Button disabled={!game.color || game.active < 2} onClick={startClick}>Start game</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GameOrder = ({table}) => {
|
const GameOrder = ({table, game}) => {
|
||||||
|
|
||||||
const rollClick = (event) => {
|
const rollClick = (event) => {
|
||||||
table.throwDice();
|
table.throwDice();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!table.game) {
|
if (!game) {
|
||||||
return (<></>);
|
return (<></>);
|
||||||
}
|
}
|
||||||
|
|
||||||
let players = [], hasRolled = true;
|
let players = [], hasRolled = true;
|
||||||
for (let color in table.game.players) {
|
for (let color in game.players) {
|
||||||
const item = table.game.players[color],
|
const item = game.players[color],
|
||||||
name = getPlayerName(table.game.sessions, color);
|
name = getPlayerName(game.sessions, color);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!item.orderRoll) {
|
if (!item.orderRoll) {
|
||||||
item.orderRoll = 0;
|
item.orderRoll = 0;
|
||||||
}
|
}
|
||||||
if (color === table.game.color) {
|
if (color === game.color) {
|
||||||
hasRolled = item.orderRoll !== 0;
|
hasRolled = item.orderRoll !== 0;
|
||||||
}
|
}
|
||||||
players.push({ name: name, color: color, ...item });
|
players.push({ name: name, color: color, ...item });
|
||||||
@ -116,7 +114,7 @@ const GameOrder = ({table}) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="GameOrder">
|
<div className="GameOrder">
|
||||||
{ table.game && <Paper>
|
{ game && <Paper>
|
||||||
<div className="Title">Game Order</div>
|
<div className="Title">Game Order</div>
|
||||||
<div className="PlayerList">
|
<div className="PlayerList">
|
||||||
{ players }
|
{ players }
|
||||||
@ -127,19 +125,19 @@ const GameOrder = ({table}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectPlayer = ({table, players}) => {
|
const SelectPlayer = ({table, game, players}) => {
|
||||||
const playerClick = (event) => {
|
const playerClick = (event) => {
|
||||||
table.stealResource(event.currentTarget.getAttribute('data-color'));
|
table.stealResource(event.currentTarget.getAttribute('data-color'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!table.game) {
|
if (!game.id) {
|
||||||
return (<></>);
|
return (<></>);
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = players.map(color => {
|
let list = players.map(color => {
|
||||||
let item = {
|
let item = {
|
||||||
color: color,
|
color: color,
|
||||||
name: getPlayerName(table.game.sessions, color)
|
name: getPlayerName(game.sessions, color)
|
||||||
};
|
};
|
||||||
return <div className="SelectPlayerItem"
|
return <div className="SelectPlayerItem"
|
||||||
onClick={playerClick}
|
onClick={playerClick}
|
||||||
@ -152,7 +150,7 @@ const SelectPlayer = ({table, players}) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SelectPlayer">
|
<div className="SelectPlayer">
|
||||||
{ table.game && <Paper>
|
{ game && <Paper>
|
||||||
<div className="Title">Select Player to Steal From</div>
|
<div className="Title">Select Player to Steal From</div>
|
||||||
<div className="SelectPlayerList">
|
<div className="SelectPlayerList">
|
||||||
{ list }
|
{ list }
|
||||||
@ -162,7 +160,7 @@ const SelectPlayer = ({table, players}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Action = ({ table }) => {
|
const Action = ({ table, game }) => {
|
||||||
const buildClicked = (event) => {
|
const buildClicked = (event) => {
|
||||||
table.buildClicked(event);
|
table.buildClicked(event);
|
||||||
};
|
};
|
||||||
@ -197,13 +195,12 @@ const Action = ({ table }) => {
|
|||||||
table.setSelected("");
|
table.setSelected("");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if (!table.game) {
|
if (!game.id) {
|
||||||
console.log("Why no game?");
|
console.log("Why no game id?");
|
||||||
return (<Paper className="Action"/>);
|
return (<Paper className="Action"/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const game = table.game,
|
const inLobby = game.state === 'lobby',
|
||||||
inLobby = game.state === 'lobby',
|
|
||||||
inGame = game.state === 'normal',
|
inGame = game.state === 'normal',
|
||||||
player = game ? game.player : undefined,
|
player = game ? game.player : undefined,
|
||||||
hasRolled = (game && game.turn && game.turn.roll) ? true : false,
|
hasRolled = (game && game.turn && game.turn.roll) ? true : false,
|
||||||
@ -216,7 +213,7 @@ const Action = ({ table }) => {
|
|||||||
return (
|
return (
|
||||||
<Paper className="Action">
|
<Paper className="Action">
|
||||||
{ inLobby && <>
|
{ inLobby && <>
|
||||||
<StartButton table={table}/>
|
<StartButton table={table} game={game}/>
|
||||||
<Button disabled={game.color ? false : true} onClick={newTableClick}>New table</Button>
|
<Button disabled={game.color ? false : true} onClick={newTableClick}>New table</Button>
|
||||||
<Button disabled={game.color ? true : false} onClick={() => {table.setState({ pickName: true})}}>Change name</Button> </> }
|
<Button disabled={game.color ? true : false} onClick={() => {table.setState({ pickName: true})}}>Change name</Button> </> }
|
||||||
{ !inLobby && <>
|
{ !inLobby && <>
|
||||||
@ -235,15 +232,15 @@ const Action = ({ table }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlayerName = ({table}) => {
|
const PlayerName = ({table, game}) => {
|
||||||
const [name, setName] = useState((table && table.game && table.game.name) ? table.game.name : "");
|
const [name, setName] = useState(game.name ? game.name : "");
|
||||||
|
|
||||||
const nameChange = (event) => {
|
const nameChange = (event) => {
|
||||||
setName(event.target.value);
|
setName(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendName = () => {
|
const sendName = () => {
|
||||||
if (name !== table.game.name) {
|
if (name !== game.name) {
|
||||||
table.setPlayerName(name);
|
table.setPlayerName(name);
|
||||||
} else {
|
} else {
|
||||||
table.setError("");
|
table.setError("");
|
||||||
@ -273,35 +270,36 @@ const PlayerName = ({table}) => {
|
|||||||
|
|
||||||
/* This needs to take in a mechanism to declare the
|
/* This needs to take in a mechanism to declare the
|
||||||
* player's active item in the game */
|
* player's active item in the game */
|
||||||
const Players = ({ table }) => {
|
const Players = ({ table, game }) => {
|
||||||
const toggleSelected = (key) => {
|
const toggleSelected = (key) => {
|
||||||
table.setSelected(table.game.color === key ? "" : key);
|
table.setSelected(game.color === key ? "" : key);
|
||||||
}
|
}
|
||||||
|
|
||||||
const players = [];
|
const players = [];
|
||||||
|
|
||||||
if (!table.game) {
|
if (!game.id) {
|
||||||
return (<></>);
|
return (<></>);
|
||||||
}
|
}
|
||||||
for (let color in table.game.players) {
|
|
||||||
const item = table.game.players[color], inLobby = table.game.state === 'lobby';
|
for (let color in game.players) {
|
||||||
|
const item = game.players[color], inLobby = game.state === 'lobby';
|
||||||
if (!inLobby && item.status === 'Not active') {
|
if (!inLobby && item.status === 'Not active') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let name = getPlayerName(table.game.sessions, color),
|
let name = getPlayerName(game.sessions, color),
|
||||||
selectable = table.game.state === 'lobby' && (item.status === 'Not active' || table.game.color === color);
|
selectable = game.state === 'lobby' && (item.status === 'Not active' || game.color === color);
|
||||||
if (!name) {
|
|
||||||
name = "Available";
|
|
||||||
}
|
|
||||||
players.push((
|
players.push((
|
||||||
<div
|
<div
|
||||||
data-selectable={selectable}
|
data-selectable={selectable}
|
||||||
data-selected={table.game.color === color}
|
data-selected={game.color === color}
|
||||||
className="PlayerEntry"
|
className="PlayerEntry"
|
||||||
onClick={() => { inLobby && selectable && toggleSelected(color) }}
|
onClick={() => { inLobby && selectable && toggleSelected(color) }}
|
||||||
key={`player-${color}`}>
|
key={`player-${color}`}>
|
||||||
<PlayerColor color={color}/>{name}
|
<PlayerColor color={color}/>{name ? name : 'Available' }
|
||||||
<MediaControl color={color} ws={table.ws} game={table.game}/>
|
{ name && <MediaContext.Provider value={game.peers}>
|
||||||
|
<MediaControl peer={name} isSelf={game.color === color}/>
|
||||||
|
</MediaContext.Provider> }
|
||||||
|
{ !name && <div></div> }
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -321,20 +319,15 @@ class Table extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
total: 0,
|
|
||||||
wood: 0,
|
|
||||||
sheep: 0,
|
|
||||||
brick: 0,
|
|
||||||
stone: 0,
|
|
||||||
wheat: 0,
|
|
||||||
game: null,
|
|
||||||
message: "",
|
message: "",
|
||||||
error: "",
|
error: "",
|
||||||
signature: "",
|
signature: "",
|
||||||
buildActive: false,
|
buildActive: false,
|
||||||
cardActive: undefined,
|
cardActive: undefined,
|
||||||
loading: 0,
|
loading: 0,
|
||||||
noNetwork: false
|
noNetwork: false,
|
||||||
|
ws: undefined,
|
||||||
|
peers: {}
|
||||||
};
|
};
|
||||||
this.componentDidMount = this.componentDidMount.bind(this);
|
this.componentDidMount = this.componentDidMount.bind(this);
|
||||||
this.throwDice = this.throwDice.bind(this);
|
this.throwDice = this.throwDice.bind(this);
|
||||||
@ -359,25 +352,19 @@ class Table extends React.Component {
|
|||||||
this.selectResources = this.selectResources.bind(this);
|
this.selectResources = this.selectResources.bind(this);
|
||||||
this.buildItem = this.buildItem.bind(this);
|
this.buildItem = this.buildItem.bind(this);
|
||||||
|
|
||||||
this.mouse = { x: 0, y: 0 };
|
|
||||||
this.radius = 0.317;
|
|
||||||
|
|
||||||
this.loadTimer = null;
|
this.loadTimer = null;
|
||||||
|
this.peers = {};
|
||||||
this.game = null;
|
|
||||||
this.pips = [];
|
|
||||||
this.tiles = [];
|
|
||||||
this.borders = [];
|
|
||||||
this.tabletop = null;
|
|
||||||
this.closest = {
|
|
||||||
info: {},
|
|
||||||
tile: null,
|
|
||||||
road: null,
|
|
||||||
tradeToken: null,
|
|
||||||
settlement: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this.id = (props.router && props.router.params.id) ? props.router.params.id : 0;
|
this.id = (props.router && props.router.params.id) ? props.router.params.id : 0;
|
||||||
|
this.setPeers = this.setPeers.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPeers(update) {
|
||||||
|
for (let key in this.peers) {
|
||||||
|
if (!(key in update)) {
|
||||||
|
delete this.peers[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({ peers: Object.assign({}, this.peers, update)});
|
||||||
}
|
}
|
||||||
|
|
||||||
closeCard() {
|
closeCard() {
|
||||||
@ -396,7 +383,7 @@ class Table extends React.Component {
|
|||||||
|
|
||||||
this.setState({ loading: this.state.loading + 1 });
|
this.setState({ loading: this.state.loading + 1 });
|
||||||
|
|
||||||
return window.fetch(`${base}/api/v1/games/${this.state.game.id}/${action}/${value}`, {
|
return window.fetch(`${base}/api/v1/games/${this.state.id}/${action}/${value}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
@ -509,7 +496,7 @@ class Table extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ loading: this.state.loading + 1 });
|
this.setState({ loading: this.state.loading + 1 });
|
||||||
return window.fetch(`${base}/api/v1/games/${this.state.game.id}/state/${state}`, {
|
return window.fetch(`${base}/api/v1/games/${this.state.id}/state/${state}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
@ -531,7 +518,7 @@ class Table extends React.Component {
|
|||||||
this.setError(error.message);
|
this.setError(error.message);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.setState({ loading: this.state.loading - 1 });
|
this.setState({ loading: this.state.loading - 1 });
|
||||||
return this.game.state;
|
return this.state.state;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,38 +571,43 @@ class Table extends React.Component {
|
|||||||
}
|
}
|
||||||
console.log("Update Game", game);
|
console.log("Update Game", game);
|
||||||
|
|
||||||
this.setState( { game } );
|
/* Only update fields that are changing */
|
||||||
this.setState(this.state);
|
for (let key in game) {
|
||||||
this.game = game;
|
if (game[key] === this.state[key]) {
|
||||||
|
delete game[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Updating: `, { ...game });
|
||||||
|
this.setState( { ...game } );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMessage() {
|
updateMessage() {
|
||||||
const player = (this.game && this.game.color) ? this.game.players[this.game.color] : undefined,
|
const player = (this.state.id && this.state.color) ? this.state.players[this.state.color] : undefined,
|
||||||
name = this.game ? this.game.name : "";
|
name = this.state ? this.state.name : "";
|
||||||
|
|
||||||
let message = <></>;
|
let message = <></>;
|
||||||
if (this.state.pickName || !name) {
|
if (this.state.pickName || !name) {
|
||||||
message = <>{message}Enter the name you would like to be known by, then press <b>ENTER</b> or select <b>SET</b>.</>;
|
message = <>{message}Enter the name you would like to be known by, then press <b>ENTER</b> or select <b>SET</b>.</>;
|
||||||
} else {
|
} else {
|
||||||
switch (this.game && this.game.state) {
|
switch (this.state.state) {
|
||||||
case 'lobby':
|
case 'lobby':
|
||||||
message = <>{message}You are in the lobby as <b>{name}</b>.</>;
|
message = <>{message}You are in the lobby as <b>{name}</b>.</>;
|
||||||
if (!this.game.color) {
|
if (!this.state.color) {
|
||||||
message = <>{message}You select one of the <b>Available</b> colors below.</>;
|
message = <>{message}You select one of the <b>Available</b> colors below.</>;
|
||||||
} else {
|
} else {
|
||||||
message = <>{message}You have selected <PlayerColor color={this.game.color}/>.</>;
|
message = <>{message}You have selected <PlayerColor color={this.state.color}/>.</>;
|
||||||
}
|
}
|
||||||
message = <>{message}You can chat with other players below.</>;
|
message = <>{message}You can chat with other players below.</>;
|
||||||
if (this.game.active < 2) {
|
if (this.state.active < 2) {
|
||||||
message = <>{message}Once there are two or more players, you can select <StartButton table={this}/>.</>;
|
message = <>{message}Once there are two or more players, you can select <StartButton table={this} game={this.state}/>.</>;
|
||||||
} else {
|
} else {
|
||||||
message = <>{message}There are enough players to start the game. Select <StartButton table={this}/> when ready.</>;
|
message = <>{message}There are enough players to start the game. Select <StartButton table={this} game={this.state}/> when ready.</>;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'game-order':
|
case 'game-order':
|
||||||
if (!player) {
|
if (!player) {
|
||||||
message = <>{message}You are an observer in this game as <b>{name}</b>.</>;
|
message = <>{message}You are an observer in this game as <b>{name}</b>.</>;
|
||||||
message = <>{message}You can chat with other players below as <b>{this.game.name}</b>, but cannot play unless players go back to the Lobby.</>;
|
message = <>{message}You can chat with other players below as <b>{this.state.name}</b>, but cannot play unless players go back to the Lobby.</>;
|
||||||
} else {
|
} else {
|
||||||
if (!player.order) {
|
if (!player.order) {
|
||||||
message = <>{message}You need to roll for game order. Click <b>Roll Dice</b> below.</>;
|
message = <>{message}You need to roll for game order. Click <b>Roll Dice</b> below.</>;
|
||||||
@ -638,33 +630,33 @@ class Table extends React.Component {
|
|||||||
message = <>{message}The game is in a wonky state. Sorry :(</>;
|
message = <>{message}The game is in a wonky state. Sorry :(</>;
|
||||||
break;
|
break;
|
||||||
case 'normal':
|
case 'normal':
|
||||||
if (this.game && this.game.turn) {
|
if (this.state.turn) {
|
||||||
if (this.game.turn.roll === 7) {
|
if (this.state.turn.roll === 7) {
|
||||||
message = <>{message}Robber was rolled!</>;
|
message = <>{message}Robber was rolled!</>;
|
||||||
let move = true;
|
let move = true;
|
||||||
for (let color in this.game.players) {
|
for (let color in this.state.players) {
|
||||||
let name = '';
|
let name = '';
|
||||||
for (let i = 0; i < this.game.sessions.length; i++) {
|
for (let i = 0; i < this.state.sessions.length; i++) {
|
||||||
if (this.game.sessions[i].color === color) {
|
if (this.state.sessions[i].color === color) {
|
||||||
name = this.game.sessions[i].name;
|
name = this.state.sessions[i].name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const discard = this.game.players[color].mustDiscard;
|
const discard = this.state.players[color].mustDiscard;
|
||||||
if (discard) {
|
if (discard) {
|
||||||
move = false;
|
move = false;
|
||||||
message = <>{message}<PlayerColor color={color}/> {name} needs to discard {discard} resources.</>;
|
message = <>{message}<PlayerColor color={color}/> {name} needs to discard {discard} resources.</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (move && (this.game.turn && !this.game.turn.placedRobber)) {
|
if (move && (this.state.turn && !this.state.turn.placedRobber)) {
|
||||||
message = <>{message}<PlayerColor color={this.game.turn.color}/> {this.game.turn.name} needs to move the robber.</>
|
message = <>{message}<PlayerColor color={this.state.turn.color}/> {this.state.turn.name} needs to move the robber.</>
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message = <>It is <PlayerColor color={this.game.turn.color}/> {this.game.turn.name}'s turn.</>;
|
message = <>It is <PlayerColor color={this.state.turn.color}/> {this.state.turn.name}'s turn.</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
message = <>{message}Game state is: {this.game.state}</>;
|
message = <>{message}Game state is: {this.state.state}</>;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,7 +671,7 @@ class Table extends React.Component {
|
|||||||
delete this.ws;
|
delete this.ws;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`${this.game.name} Resetting keep-alive. Last ping: ${(Date.now() - this.lastPing) / 1000}`);
|
// console.log(`${this.game.name} Resetting keep-alive. Last ping: ${(Date.now() - this.lastPing) / 1000}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.keepAlive) {
|
if (this.keepAlive) {
|
||||||
@ -690,7 +682,7 @@ class Table extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.keepAlive = setTimeout(() => {
|
this.keepAlive = setTimeout(() => {
|
||||||
console.log(`${this.game.name} No ping after 10 seconds. Last ping: ${(Date.now() - this.lastPing) / 1000}`);
|
console.log(`${this.state.name} No ping after 10 seconds. Last ping: ${(Date.now() - this.lastPing) / 1000}`);
|
||||||
this.setState({ noNetwork: true });
|
this.setState({ noNetwork: true });
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
@ -707,7 +699,7 @@ class Table extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connectWebSocket() {
|
connectWebSocket() {
|
||||||
if (!this.game) {
|
if (!this.state.id) {
|
||||||
console.log(`Cannot initiate websocket connection while no game is set.`);
|
console.log(`Cannot initiate websocket connection while no game is set.`);
|
||||||
this.resetKeepAlive(true);
|
this.resetKeepAlive(true);
|
||||||
return;
|
return;
|
||||||
@ -721,11 +713,12 @@ class Table extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
new_uri = "ws";
|
new_uri = "ws";
|
||||||
}
|
}
|
||||||
new_uri = `${new_uri}://${loc.host}${base}/api/v1/games/ws/${this.game.id}/`;
|
new_uri = `${new_uri}://${loc.host}${base}/api/v1/games/ws/${this.state.id}/`;
|
||||||
console.log(`Attempting WebSocket connection to ${new_uri}`);
|
console.log(`Attempting WebSocket connection to ${new_uri}`);
|
||||||
|
|
||||||
this.ws = new WebSocket(new_uri);
|
this.ws = new WebSocket(new_uri);
|
||||||
this.lastPing = this.game.timestamp;
|
this.setState({ ws: this.ws });
|
||||||
|
this.lastPing = this.state.timestamp;
|
||||||
|
|
||||||
this.ws.addEventListener('message', (event) => {
|
this.ws.addEventListener('message', (event) => {
|
||||||
this.resetKeepAlive();
|
this.resetKeepAlive();
|
||||||
@ -758,18 +751,18 @@ class Table extends React.Component {
|
|||||||
|
|
||||||
this.ws.addEventListener('error', (event) => {
|
this.ws.addEventListener('error', (event) => {
|
||||||
this.setState({ error: event.message });
|
this.setState({ error: event.message });
|
||||||
console.error(`${this.game.name} WebSocket error: ${(Date.now() - this.game.lastPing) / 1000}`);
|
console.error(`${this.state.name} WebSocket error: ${(Date.now() - this.state.lastPing) / 1000}`);
|
||||||
this.resetKeepAlive(true);
|
this.resetKeepAlive(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ws.addEventListener('close', (event) => {
|
this.ws.addEventListener('close', (event) => {
|
||||||
console.log(`${this.game.name} WebSocket close: ${(Date.now() - this.game.lastPing) / 1000}`);
|
console.log(`${this.state.name} WebSocket close: ${(Date.now() - this.state.lastPing) / 1000}`);
|
||||||
this.setState({ error: event.message });
|
this.setState({ error: event.message });
|
||||||
this.resetKeepAlive(true);
|
this.resetKeepAlive(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ws.addEventListener('open', (event) => {
|
this.ws.addEventListener('open', (event) => {
|
||||||
console.log(`${this.game.name} WebSocket open: Sending game-update request: ${(Date.now() - this.lastPing) / 1000}`);
|
console.log(`${this.state.name} WebSocket open: Sending game-update request: ${(Date.now() - this.lastPing) / 1000}`);
|
||||||
this.ws.send(JSON.stringify({ type: 'game-update' }));
|
this.ws.send(JSON.stringify({ type: 'game-update' }));
|
||||||
this.resetKeepAlive();
|
this.resetKeepAlive();
|
||||||
});
|
});
|
||||||
@ -877,15 +870,11 @@ class Table extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cardClicked(card) {
|
cardClicked(card) {
|
||||||
const game = this.state.game;
|
|
||||||
if (!game) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({cardActive: card });
|
this.setState({cardActive: card });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const game = this.state.game,
|
const game = this.state,
|
||||||
player = game ? game.player : undefined,
|
player = game ? game.player : undefined,
|
||||||
isTurn = (game && game.turn && game.turn.color === game.color) ? true : false,
|
isTurn = (game && game.turn && game.turn.color === game.color) ? true : false,
|
||||||
showMessage = (game && (game.state === 'lobby' || !game.name));
|
showMessage = (game && (game.state === 'lobby' || !game.name));
|
||||||
@ -928,26 +917,30 @@ class Table extends React.Component {
|
|||||||
development = <>/</>;
|
development = <>/</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.game) {
|
if (!this.state.id) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<MediaAgent table={this}/>
|
<MediaContext.Provider value={{peers: this.peers, setPeers: this.setPeers}}>
|
||||||
|
<MediaAgent ws={this.state.ws} name={this.state.name}/>
|
||||||
|
</MediaContext.Provider>
|
||||||
<div className="Table">
|
<div className="Table">
|
||||||
{ this.state.loading > 0 && <CircularProgress className='Loading'/> }
|
{ this.state.loading > 0 && <CircularProgress className='Loading'/> }
|
||||||
|
|
||||||
{ this.state.noNetwork && <div className='NoNetwork'/> }
|
{ this.state.noNetwork && <div className='NoNetwork'/> }
|
||||||
|
|
||||||
{ this.game && <Activities table={this}/> }
|
<Activities table={this.state}/>
|
||||||
|
|
||||||
<div style={{display: "inline-flex", flex: 1, flexDirection: "column"}}>
|
<div style={{display: "inline-flex", flex: 1, flexDirection: "column"}}>
|
||||||
<Board table={this} game={game}/>
|
<Board table={this} game={this.state}/>
|
||||||
{ player !== undefined && <>
|
{ player !== undefined && <>
|
||||||
<PlayersStatus table={this}/>
|
<PlayersStatus table={this} game={this.state}/>
|
||||||
<PlayersStatus active={true}
|
<PlayersStatus active={true}
|
||||||
onClick={this.buildItem}
|
onClick={this.buildItem}
|
||||||
table={this} color={this.game.color}/>
|
table={this}
|
||||||
|
game={this.state}
|
||||||
|
color={this.state.color}/>
|
||||||
<div className="BottomBar">
|
<div className="BottomBar">
|
||||||
<div className="Hand">
|
<div className="Hand">
|
||||||
<Resource type="wood" count={player.wood}/>
|
<Resource type="wood" count={player.wood}/>
|
||||||
@ -984,11 +977,11 @@ class Table extends React.Component {
|
|||||||
|
|
||||||
{ game && <div className={'Game ' + game.state}>
|
{ game && <div className={'Game ' + game.state}>
|
||||||
{ showMessage && <Paper className="Message">{ this.state.message }</Paper> }
|
{ showMessage && <Paper className="Message">{ this.state.message }</Paper> }
|
||||||
{(this.state.pickName || !game.name) && <PlayerName table={this}/> }
|
{(this.state.pickName || !game.name) && <PlayerName table={this} game={this.state}/> }
|
||||||
{(!this.state.pickName && game.name) && <>
|
{(!this.state.pickName && game.name) && <>
|
||||||
<Players table={this}/>
|
<Players table={this} game={this.state}/>
|
||||||
<Chat table={this}/>
|
<Chat table={this} game={this.state}/>
|
||||||
<Action table={this}/>
|
<Action table={this} game={this.state}/>
|
||||||
</> }
|
</> }
|
||||||
</div> }
|
</div> }
|
||||||
|
|
||||||
@ -1020,7 +1013,7 @@ class Table extends React.Component {
|
|||||||
game.turn &&
|
game.turn &&
|
||||||
isTurn &&
|
isTurn &&
|
||||||
game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1 &&
|
game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1 &&
|
||||||
<SelectPlayer table={this} players={game.turn.limits.players}/>
|
<SelectPlayer table={this} game={this.state} players={game.turn.limits.players}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{ this.state.error && <Paper onClick={() => this.setState({ error: undefined })} className="Error"><div>{this.state.error}</div></Paper> }
|
{ this.state.error && <Paper onClick={() => this.setState({ error: undefined })} className="Error"><div>{this.state.error}</div></Paper> }
|
||||||
|
@ -92,7 +92,9 @@ const staticData = {
|
|||||||
[ undefined, "stone", undefined ]
|
[ undefined, "stone", undefined ]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const games = {};
|
const games = {};
|
||||||
|
const audio = {};
|
||||||
|
|
||||||
const processTies = (players) => {
|
const processTies = (players) => {
|
||||||
players.sort((A, B) => {
|
players.sort((A, B) => {
|
||||||
@ -470,7 +472,7 @@ const getSession = (game, session) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!session.player_id) {
|
if (!session.player_id) {
|
||||||
session.player_id = crypto.randomBytes(32).toString('hex');
|
session.player_id = crypto.randomBytes(16).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = session.player_id;
|
const id = session.player_id;
|
||||||
@ -782,6 +784,8 @@ const setPlayerName = (game, session, name) => {
|
|||||||
if (session.color) {
|
if (session.color) {
|
||||||
return `You cannot change your name while you have a color selected.`;
|
return `You cannot change your name while you have a color selected.`;
|
||||||
}
|
}
|
||||||
|
const id = game.id;
|
||||||
|
|
||||||
let rejoin = false;
|
let rejoin = false;
|
||||||
/* Check to ensure name is not already in use */
|
/* Check to ensure name is not already in use */
|
||||||
if (game && name) for (let key in game.sessions) {
|
if (game && name) for (let key in game.sessions) {
|
||||||
@ -811,22 +815,33 @@ const setPlayerName = (game, session, name) => {
|
|||||||
const old = session.name;
|
const old = session.name;
|
||||||
let message;
|
let message;
|
||||||
|
|
||||||
session.name = name;
|
if (!name) {
|
||||||
|
|
||||||
if (name) {
|
|
||||||
if (!old) {
|
|
||||||
message = `A new player has entered the lobby as ${name}.`;
|
|
||||||
} else {
|
|
||||||
if (rejoin) {
|
|
||||||
message = `${name} has rejoined the game! Welcome back, ${name}.`;
|
|
||||||
} else {
|
|
||||||
message = `${old} has changed their name to ${name}.`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return `You can not set your name to nothing!`;
|
return `You can not set your name to nothing!`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!old) {
|
||||||
|
message = `A new player has entered the lobby as ${name}.`;
|
||||||
|
session.name = name;
|
||||||
|
} else {
|
||||||
|
if (rejoin) {
|
||||||
|
message = `${name} has rejoined the game! Welcome back, ${name}.`;
|
||||||
|
session.name = name;
|
||||||
|
if (session.ws && id in audio) {
|
||||||
|
part(audio[id], session, game.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = `${old} has changed their name to ${name}.`;
|
||||||
|
if (session.ws && id in audio) {
|
||||||
|
part(audio[id], session, game.id);
|
||||||
|
}
|
||||||
|
session.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.ws) {
|
||||||
|
join(audio[id], session, game.id);
|
||||||
|
}
|
||||||
|
|
||||||
addChatMessage(game, null, message);
|
addChatMessage(game, null, message);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -2719,28 +2734,59 @@ const resetDisconnectCheck = (game, req) => {
|
|||||||
//req.disconnectCheck = setTimeout(() => { wsInactive(game, req) }, 20000);
|
//req.disconnectCheck = setTimeout(() => { wsInactive(game, req) }, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const channels = {},
|
const join = (peers, session, id) => {
|
||||||
sockets = {};
|
const ws = session.ws;
|
||||||
|
|
||||||
const part = (ws, channel) => {
|
if (!session.name) {
|
||||||
console.log(`[${ws.id}] part `);
|
console.error(`${id}:${session.id} - join - No name set yet. Audio not available.`);
|
||||||
|
|
||||||
if (!(channel in ws.channels)) {
|
|
||||||
console.log(`[${ws.id}] ERROR: not in `, channel);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete ws.channels[channel];
|
console.log(`${id} - join - ${session.name}`);
|
||||||
delete channels[channel][id];
|
|
||||||
|
if (session.name in peers) {
|
||||||
|
console.log(`${id}:${session.name} - Already joined to Audio.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (id in channels[channel]) {
|
for (let peer in peers) {
|
||||||
channels[channel][id].send(JSON.stringify({
|
peers[peer].send(JSON.stringify({
|
||||||
|
type: 'addPeer',
|
||||||
|
data: { 'peer_id': peer, 'should_create_offer': false }
|
||||||
|
}));
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'addPeer',
|
||||||
|
data: {'peer_id': peer, 'should_create_offer': true}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add this user as a peer connected to this WebSocket */
|
||||||
|
peers[session.name] = ws;
|
||||||
|
};
|
||||||
|
|
||||||
|
const part = (peers, session, id) => {
|
||||||
|
const ws = session.ws;
|
||||||
|
|
||||||
|
console.log(`${id}:${session.name} - Audio part.`);
|
||||||
|
|
||||||
|
if (!(session.name in peers)) {
|
||||||
|
console.log(`${id}:${session.name} - Does not exist in game audio.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete peers[session.name];
|
||||||
|
|
||||||
|
/* Remove this peer from all other peers, and remove each
|
||||||
|
* peer from this peer */
|
||||||
|
for (let peer in peers) {
|
||||||
|
peers[peer].send(JSON.stringify({
|
||||||
type: 'removePeer',
|
type: 'removePeer',
|
||||||
data: {'peer_id': id}
|
data: {'peer_id': peer}
|
||||||
}));
|
}));
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'removePeer',
|
type: 'removePeer',
|
||||||
data: {'peer_id': id}
|
data: {'peer_id': peer}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2749,12 +2795,13 @@ router.ws("/ws/:id", async (ws, req) => {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
ws.id = req.session.player_id;
|
ws.id = req.session.player_id;
|
||||||
ws.channels = {},
|
|
||||||
|
|
||||||
sockets[ws.id] = ws;
|
if (!(id in audio)) {
|
||||||
|
audio[id] = {}; /* List of peer sockets using session.name as index. */
|
||||||
|
console.log(`${id} - New Game Audio`);
|
||||||
console.log(`Connection for ${ws.id}`);
|
} else {
|
||||||
|
console.log(`${id} - Already has Audio`);
|
||||||
|
}
|
||||||
|
|
||||||
/* Setup WebSocket event handlers prior to performing any async calls or
|
/* Setup WebSocket event handlers prior to performing any async calls or
|
||||||
* we may miss the first messages from clients */
|
* we may miss the first messages from clients */
|
||||||
@ -2782,93 +2829,88 @@ router.ws("/ws/:id", async (ws, req) => {
|
|||||||
const game = await loadGame(id);
|
const game = await loadGame(id);
|
||||||
if (game) {
|
if (game) {
|
||||||
const session = getSession(game, req.session);
|
const session = getSession(game, req.session);
|
||||||
console.log(`WebSocket closed for ${session.name}`);
|
|
||||||
if (session && session.ws) {
|
if (session && session.ws) {
|
||||||
|
/* Cleanup any voice channels */
|
||||||
|
if (id in audio) {
|
||||||
|
part(audio[id], session, id);
|
||||||
|
}
|
||||||
session.ws.close();
|
session.ws.close();
|
||||||
session.ws = undefined;
|
session.ws = undefined;
|
||||||
|
console.log(`WebSocket closed for ${session.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Cleanup any voice channels */
|
|
||||||
for (let channel in ws.channels) {
|
console.log(`${id}:${ws.id} - closed connection`);
|
||||||
part(channel);
|
|
||||||
}
|
|
||||||
console.log(`[${ws.id}] disconnected`);
|
|
||||||
delete sockets[ws.id];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('message', async (message) => {
|
ws.on('message', async (message) => {
|
||||||
const data = JSON.parse(message);
|
const data = JSON.parse(message);
|
||||||
|
const game = await loadGame(id);
|
||||||
|
if (!game) {
|
||||||
|
console.error(`Unable to load/create new game for WS request.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const session = getSession(game, req.session);
|
||||||
|
if (!session) {
|
||||||
|
console.error(`Unable to obtain session.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session.ws) {
|
||||||
|
session.ws = ws;
|
||||||
|
}
|
||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'join': {
|
case 'join':
|
||||||
const { channel, userdata } = data.config;
|
join(audio[id], session, id);
|
||||||
console.log(`[${ws.id}] join `, channel);
|
|
||||||
|
|
||||||
if (channel in ws.channels) {
|
|
||||||
console.log(`[${ws.id}] ERROR: already joined `, channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(channel in channels)) {
|
|
||||||
channels[channel] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let id in channels[channel]) {
|
|
||||||
channels[channel][id].send(JSON.stringify({
|
|
||||||
type: 'addPeer',
|
|
||||||
data: { 'peer_id': id, 'should_create_offer': false }
|
|
||||||
}));
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'addPeer',
|
|
||||||
data: {'peer_id': id, 'should_create_offer': true}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
channels[channel][ws.id] = ws;
|
|
||||||
ws.channels[channel] = channel;
|
|
||||||
} break;
|
|
||||||
case 'part':
|
|
||||||
part(ws, data.part);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'part':
|
||||||
|
part(audio[id], session, id);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'relayICECandidate': {
|
case 'relayICECandidate': {
|
||||||
|
if (!(id in audio)) {
|
||||||
|
console.error(`${id} - relayICECandidate - Does not have Audio`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { peer_id, ice_candidate } = data.config;
|
const { peer_id, ice_candidate } = data.config;
|
||||||
console.log(`[${ws.id}] relaying ICE candidate to [${peer_id}] `, ice_candidate);
|
console.log(`${id} - relayICECandidate ${session.name} to ${peer_id}`,
|
||||||
|
ice_candidate);
|
||||||
|
|
||||||
if (peer_id in sockets) {
|
if (peer_id in audio[id]) {
|
||||||
sockets[peer_id].send(JSON.stringify({
|
audio[id][peer_id].send(JSON.stringify({
|
||||||
type: 'iceCandidate',
|
type: 'iceCandidate',
|
||||||
data: {'peer_id': ws.id, 'ice_candidate': ice_candidate }
|
data: {'peer_id': session.name, 'ice_candidate': ice_candidate }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 'relaySessionDescription': {
|
case 'relaySessionDescription': {
|
||||||
|
if (!(id in audio)) {
|
||||||
|
console.error(`${id} - relaySessionDescription - Does not have Audio`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { peer_id, session_description } = data.config;
|
const { peer_id, session_description } = data.config;
|
||||||
console.log(`[${ws.id}] relaying session description to [${peer_id}] `, session_description);
|
console.log(`${id} - relaySessionDescription ${session.name} to ${peer_id}`,
|
||||||
if (peer_id in sockets) {
|
session_description);
|
||||||
sockets[peer_id].send(JSON.stringify({
|
if (peer_id in audio[id]) {
|
||||||
|
audio[id][peer_id].send(JSON.stringify({
|
||||||
type: 'sessionDescription',
|
type: 'sessionDescription',
|
||||||
data: {'peer_id': ws.id, 'session_description': session_description }
|
data: {'peer_id': session.name, 'session_description': session_description }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 'pong': {
|
|
||||||
const game = await loadGame(id);
|
case 'pong':
|
||||||
if (!game) {
|
|
||||||
console.error(`Unable to load/create new game for WS request.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resetDisconnectCheck(game, req);
|
resetDisconnectCheck(game, req);
|
||||||
} break;
|
break;
|
||||||
case 'game-update': {
|
case 'game-update':
|
||||||
const game = await loadGame(id);
|
|
||||||
if (!game) {
|
|
||||||
console.error(`Unable to load/create new game for WS request.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`Player ${session.name ? session.name : 'Unnamed'} requested a game update.`);
|
console.log(`Player ${session.name ? session.name : 'Unnamed'} requested a game update.`);
|
||||||
resetDisconnectCheck(game, req);
|
resetDisconnectCheck(game, req);
|
||||||
sendGame(req, undefined, game, undefined, ws);
|
sendGame(req, undefined, game, undefined, ws);
|
||||||
} break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3284,7 +3326,7 @@ const createGame = (id) => {
|
|||||||
addChatMessage(game, null, `New game created with Beginner's Layout: ${game.id}`);
|
addChatMessage(game, null, `New game created with Beginner's Layout: ${game.id}`);
|
||||||
|
|
||||||
games[game.id] = game;
|
games[game.id] = game;
|
||||||
|
audio[game.id] = {};
|
||||||
return game;
|
return game;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user