mirror of https://github.com/bra1n/townsquare.git
moved latency calculation server-side
made host per channel exclusive added auto-reconnection
This commit is contained in:
parent
919abaa0eb
commit
edea024220
|
@ -17,6 +17,7 @@ const wss = new WebSocket.Server({
|
|||
function noop() {}
|
||||
|
||||
function heartbeat() {
|
||||
this.latency = Math.round((new Date().getTime() - this.pingStart) / 2);
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
|
@ -25,11 +26,32 @@ wss.on("connection", function connection(ws, req) {
|
|||
.split("/")
|
||||
.pop()
|
||||
.toLocaleLowerCase();
|
||||
if (ws.channel.match(/-host$/i)) {
|
||||
ws.isHost = true;
|
||||
ws.channel = ws.channel.substr(0, ws.channel.length - 5);
|
||||
// check for another host on this channel
|
||||
if (
|
||||
Array.from(wss.clients).some(
|
||||
client =>
|
||||
client !== ws &&
|
||||
client.readyState === WebSocket.OPEN &&
|
||||
client.channel === ws.channel &&
|
||||
client.isHost
|
||||
)
|
||||
) {
|
||||
console.log(ws.channel, "duplicate host");
|
||||
ws.close(1000, `The channel "${ws.channel}" already has a host`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ws.isAlive = true;
|
||||
ws.pingStart = new Date().getTime();
|
||||
ws.ping(noop);
|
||||
ws.on("pong", heartbeat);
|
||||
ws.on("message", function incoming(data) {
|
||||
if (!data.match(/^\["ping/i)) {
|
||||
console.log(ws.channel, wss.clients.size, data);
|
||||
const isPing = data.match(/^\["ping/i);
|
||||
if (!isPing) {
|
||||
console.log(new Date(), wss.clients.size, ws.channel, data);
|
||||
}
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (
|
||||
|
@ -37,7 +59,12 @@ wss.on("connection", function connection(ws, req) {
|
|||
client.readyState === WebSocket.OPEN &&
|
||||
client.channel === ws.channel
|
||||
) {
|
||||
client.send(data);
|
||||
// inject latency between both clients if ping message
|
||||
if (isPing && client.latency && ws.latency) {
|
||||
client.send(data.replace(/latency/, client.latency + ws.latency));
|
||||
} else {
|
||||
client.send(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -47,6 +74,7 @@ const interval = setInterval(function ping() {
|
|||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) return ws.terminate();
|
||||
ws.isAlive = false;
|
||||
ws.pingStart = new Date().getTime();
|
||||
ws.ping(noop);
|
||||
});
|
||||
}, 30000);
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
<Screenshot ref="screenshot"></Screenshot>
|
||||
<span
|
||||
class="session"
|
||||
:class="{ spectator: session.isSpectator }"
|
||||
:class="{
|
||||
spectator: session.isSpectator,
|
||||
reconnecting: session.isReconnecting
|
||||
}"
|
||||
v-if="session.sessionId"
|
||||
@click="leaveSession"
|
||||
:title="
|
||||
|
@ -334,6 +337,16 @@ export default {
|
|||
&.spectator {
|
||||
color: $townsfolk;
|
||||
}
|
||||
&.reconnecting {
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ const handleVote = (state, [index, vote]) => {
|
|||
const state = () => ({
|
||||
sessionId: "",
|
||||
isSpectator: false,
|
||||
isReconnecting: false,
|
||||
playerCount: 0,
|
||||
ping: 0,
|
||||
playerId: "",
|
||||
|
@ -38,6 +39,7 @@ const mutations = {
|
|||
setSessionId: set("sessionId"),
|
||||
setPlayerId: set("playerId"),
|
||||
setSpectator: set("isSpectator"),
|
||||
setReconnecting: set("isReconnecting"),
|
||||
setPlayerCount: set("playerCount"),
|
||||
setPing: set("ping"),
|
||||
setVotingSpeed: set("votingSpeed"),
|
||||
|
|
|
@ -2,7 +2,7 @@ import rolesJSON from "../roles.json";
|
|||
|
||||
class LiveSession {
|
||||
constructor(store) {
|
||||
// this._wss = "ws://localhost:8081/";
|
||||
//this._wss = "ws://localhost:8081/";
|
||||
this._wss = "wss://baumgart.biz:8080/";
|
||||
this._socket = null;
|
||||
this._isSpectator = true;
|
||||
|
@ -10,6 +10,7 @@ class LiveSession {
|
|||
this._store = store;
|
||||
this._pingInterval = 30 * 1000; // 30 seconds between pings
|
||||
this._pingTimer = null;
|
||||
this._reconnectTimer = null;
|
||||
this._players = {}; // map of players connected to a session
|
||||
this._pings = {}; // map of player IDs to ping
|
||||
// reconnect to previous session
|
||||
|
@ -25,14 +26,26 @@ class LiveSession {
|
|||
*/
|
||||
_open(channel) {
|
||||
this.disconnect();
|
||||
this._socket = new WebSocket(this._wss + channel);
|
||||
this._socket = new WebSocket(
|
||||
this._wss + channel + (this._isSpectator ? "" : "-host")
|
||||
);
|
||||
this._socket.addEventListener("message", this._handleMessage.bind(this));
|
||||
this._socket.onopen = this._onOpen.bind(this);
|
||||
this._socket.onclose = () => {
|
||||
this._socket.onclose = err => {
|
||||
this._socket = null;
|
||||
this._store.commit("session/setSessionId", "");
|
||||
clearInterval(this._pingTimer);
|
||||
this._pingTimer = null;
|
||||
if (err.code !== 1000) {
|
||||
// connection interrupted, reconnect after 3 seconds
|
||||
this._store.commit("session/setReconnecting", true);
|
||||
this._reconnectTimer = setTimeout(
|
||||
() => this.connect(channel),
|
||||
3 * 1000
|
||||
);
|
||||
} else {
|
||||
this._store.commit("session/setSessionId", "");
|
||||
if (err.reason) alert(err.reason);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -69,7 +82,7 @@ class LiveSession {
|
|||
this._send("ping", [
|
||||
this._isSpectator,
|
||||
this._store.state.session.playerId,
|
||||
new Date().getTime()
|
||||
"latency"
|
||||
]);
|
||||
this._handlePing();
|
||||
clearTimeout(this._pingTimer);
|
||||
|
@ -168,9 +181,11 @@ class LiveSession {
|
|||
this._pings = {};
|
||||
this._store.commit("session/setPlayerCount", 0);
|
||||
this._store.commit("session/setPing", 0);
|
||||
this._store.commit("session/setReconnecting", false);
|
||||
clearTimeout(this._reconnectTimer);
|
||||
if (this._socket) {
|
||||
this._send("bye", this._store.state.session.playerId);
|
||||
this._socket.close();
|
||||
this._socket.close(1000);
|
||||
this._socket = null;
|
||||
}
|
||||
}
|
||||
|
@ -378,7 +393,7 @@ class LiveSession {
|
|||
* @param timestamp
|
||||
* @private
|
||||
*/
|
||||
_handlePing([isSpectator, playerId, timestamp] = []) {
|
||||
_handlePing([isSpectator, playerId, latency] = []) {
|
||||
const now = new Date().getTime();
|
||||
// remove players that haven't sent a ping in twice the timespan
|
||||
for (let player in this._players) {
|
||||
|
@ -404,10 +419,12 @@ class LiveSession {
|
|||
alert("Another storyteller joined the session!");
|
||||
} else if (this._isSpectator && !isSpectator) {
|
||||
// ping to ST
|
||||
this._store.commit("session/setPing", now - timestamp);
|
||||
} else {
|
||||
if (parseInt(latency, 10)) {
|
||||
this._store.commit("session/setPing", parseInt(latency, 10));
|
||||
}
|
||||
} else if(parseInt(latency, 10)) {
|
||||
// ping to Players
|
||||
this._pings[playerId] = now - timestamp;
|
||||
this._pings[playerId] = parseInt(latency, 10);
|
||||
const pings = Object.values(this._pings);
|
||||
this._store.commit(
|
||||
"session/setPing",
|
||||
|
|
Loading…
Reference in New Issue