mirror of https://github.com/bra1n/townsquare.git
Merge pull request #109 from bra1n/network-refactoring
Network refactoring
This commit is contained in:
commit
142968bf8a
|
@ -150,40 +150,63 @@ wss.on("connection", function connection(ws, req) {
|
||||||
.substr(1)
|
.substr(1)
|
||||||
.split(",", 1)
|
.split(",", 1)
|
||||||
.pop();
|
.pop();
|
||||||
// don't log ping messages
|
switch (messageType) {
|
||||||
if (messageType !== '"ping"') {
|
case '"ping"':
|
||||||
console.log(new Date(), wss.clients.size, ws.channel, ws.playerId, data);
|
// ping messages will only be sent host -> all or all -> host
|
||||||
}
|
|
||||||
// handle "direct" messages differently
|
|
||||||
if (messageType === '"direct"') {
|
|
||||||
try {
|
|
||||||
const dataToPlayer = JSON.parse(data)[1];
|
|
||||||
channels[ws.channel].forEach(function each(client) {
|
channels[ws.channel].forEach(function each(client) {
|
||||||
if (
|
if (
|
||||||
client !== ws &&
|
client !== ws &&
|
||||||
client.readyState === WebSocket.OPEN &&
|
client.readyState === WebSocket.OPEN &&
|
||||||
dataToPlayer[client.playerId]
|
(ws.playerId === "host" || client.playerId === "host")
|
||||||
) {
|
) {
|
||||||
client.send(JSON.stringify(dataToPlayer[client.playerId]));
|
client.send(
|
||||||
|
data.replace(/latency/, (client.latency || 0) + (ws.latency || 0))
|
||||||
|
);
|
||||||
metrics.messages_outgoing.inc();
|
metrics.messages_outgoing.inc();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
break;
|
||||||
console.log("error parsing direct message JSON", e);
|
case '"direct"':
|
||||||
}
|
// handle "direct" messages differently
|
||||||
} else {
|
console.log(
|
||||||
// all other messages
|
new Date(),
|
||||||
channels[ws.channel].forEach(function each(client) {
|
wss.clients.size,
|
||||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
ws.channel,
|
||||||
// inject latency between both clients if ping message
|
ws.playerId,
|
||||||
if (messageType === '"ping"' && client.latency && ws.latency) {
|
data
|
||||||
client.send(data.replace(/latency/, client.latency + ws.latency));
|
);
|
||||||
} else {
|
try {
|
||||||
client.send(data);
|
const dataToPlayer = JSON.parse(data)[1];
|
||||||
}
|
channels[ws.channel].forEach(function each(client) {
|
||||||
metrics.messages_outgoing.inc();
|
if (
|
||||||
|
client !== ws &&
|
||||||
|
client.readyState === WebSocket.OPEN &&
|
||||||
|
dataToPlayer[client.playerId]
|
||||||
|
) {
|
||||||
|
client.send(JSON.stringify(dataToPlayer[client.playerId]));
|
||||||
|
metrics.messages_outgoing.inc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error parsing direct message JSON", e);
|
||||||
}
|
}
|
||||||
});
|
break;
|
||||||
|
default:
|
||||||
|
// all other messages
|
||||||
|
console.log(
|
||||||
|
new Date(),
|
||||||
|
wss.clients.size,
|
||||||
|
ws.channel,
|
||||||
|
ws.playerId,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
channels[ws.channel].forEach(function each(client) {
|
||||||
|
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send(data);
|
||||||
|
metrics.messages_outgoing.inc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,13 +62,33 @@ class LiveSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message directly to a single playerId, if provided.
|
||||||
|
* Otherwise broadcast it.
|
||||||
|
* @param playerId player ID or "host", optional
|
||||||
|
* @param command
|
||||||
|
* @param params
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_sendDirect(playerId, command, params) {
|
||||||
|
if (playerId) {
|
||||||
|
this._send("direct", { [playerId]: [command, params] });
|
||||||
|
} else {
|
||||||
|
this._send(command, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open event handler for socket.
|
* Open event handler for socket.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onOpen() {
|
_onOpen() {
|
||||||
if (this._isSpectator) {
|
if (this._isSpectator) {
|
||||||
this._send("req", "gs");
|
this._sendDirect(
|
||||||
|
"host",
|
||||||
|
"getGamestate",
|
||||||
|
this._store.state.session.playerId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.sendGamestate();
|
this.sendGamestate();
|
||||||
}
|
}
|
||||||
|
@ -80,12 +100,13 @@ class LiveSession {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_ping() {
|
_ping() {
|
||||||
|
this._handlePing();
|
||||||
this._send("ping", [
|
this._send("ping", [
|
||||||
this._isSpectator,
|
this._isSpectator
|
||||||
this._store.state.session.playerId,
|
? this._store.state.session.playerId
|
||||||
|
: Object.keys(this._players).length,
|
||||||
"latency"
|
"latency"
|
||||||
]);
|
]);
|
||||||
this._handlePing();
|
|
||||||
clearTimeout(this._pingTimer);
|
clearTimeout(this._pingTimer);
|
||||||
this._pingTimer = setTimeout(this._ping.bind(this), this._pingInterval);
|
this._pingTimer = setTimeout(this._ping.bind(this), this._pingInterval);
|
||||||
}
|
}
|
||||||
|
@ -103,10 +124,8 @@ class LiveSession {
|
||||||
console.log("unsupported socket message", data);
|
console.log("unsupported socket message", data);
|
||||||
}
|
}
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case "req":
|
case "getGamestate":
|
||||||
if (params === "gs") {
|
this.sendGamestate(params);
|
||||||
this.sendGamestate();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "edition":
|
case "edition":
|
||||||
this._updateEdition(params);
|
this._updateEdition(params);
|
||||||
|
@ -204,7 +223,9 @@ class LiveSession {
|
||||||
this._store.commit("session/setReconnecting", false);
|
this._store.commit("session/setReconnecting", false);
|
||||||
clearTimeout(this._reconnectTimer);
|
clearTimeout(this._reconnectTimer);
|
||||||
if (this._socket) {
|
if (this._socket) {
|
||||||
this._send("bye", this._store.state.session.playerId);
|
if (this._isSpectator) {
|
||||||
|
this._sendDirect("host", "bye", this._store.state.session.playerId);
|
||||||
|
}
|
||||||
this._socket.close(1000);
|
this._socket.close(1000);
|
||||||
this._socket = null;
|
this._socket = null;
|
||||||
}
|
}
|
||||||
|
@ -213,9 +234,10 @@ class LiveSession {
|
||||||
/**
|
/**
|
||||||
* Publish the current gamestate.
|
* Publish the current gamestate.
|
||||||
* Optional param to reduce traffic. (send only player data)
|
* Optional param to reduce traffic. (send only player data)
|
||||||
|
* @param playerId
|
||||||
* @param isLightweight
|
* @param isLightweight
|
||||||
*/
|
*/
|
||||||
sendGamestate(isLightweight = false) {
|
sendGamestate(playerId = "", isLightweight = false) {
|
||||||
if (this._isSpectator) return;
|
if (this._isSpectator) return;
|
||||||
this._gamestate = this._store.state.players.players.map(player => ({
|
this._gamestate = this._store.state.players.players.map(player => ({
|
||||||
name: player.name,
|
name: player.name,
|
||||||
|
@ -227,12 +249,15 @@ class LiveSession {
|
||||||
: {})
|
: {})
|
||||||
}));
|
}));
|
||||||
if (isLightweight) {
|
if (isLightweight) {
|
||||||
this._send("gs", { gamestate: this._gamestate, isLightweight });
|
this._sendDirect(playerId, "gs", {
|
||||||
|
gamestate: this._gamestate,
|
||||||
|
isLightweight
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const { session, grimoire } = this._store.state;
|
const { session, grimoire } = this._store.state;
|
||||||
const { fabled } = this._store.state.players;
|
const { fabled } = this._store.state.players;
|
||||||
this.sendEdition();
|
this.sendEdition(playerId);
|
||||||
this._send("gs", {
|
this._sendDirect(playerId, "gs", {
|
||||||
gamestate: this._gamestate,
|
gamestate: this._gamestate,
|
||||||
isNight: grimoire.isNight,
|
isNight: grimoire.isNight,
|
||||||
nomination: session.nomination,
|
nomination: session.nomination,
|
||||||
|
@ -322,15 +347,16 @@ class LiveSession {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish an edition update. ST only
|
* Publish an edition update. ST only
|
||||||
|
* @param playerId
|
||||||
*/
|
*/
|
||||||
sendEdition() {
|
sendEdition(playerId = "") {
|
||||||
if (this._isSpectator) return;
|
if (this._isSpectator) return;
|
||||||
const { edition } = this._store.state;
|
const { edition } = this._store.state;
|
||||||
let roles;
|
let roles;
|
||||||
if (!edition.isOfficial) {
|
if (!edition.isOfficial) {
|
||||||
roles = Array.from(this._store.state.roles.keys());
|
roles = Array.from(this._store.state.roles.keys());
|
||||||
}
|
}
|
||||||
this._send("edition", {
|
this._sendDirect(playerId, "edition", {
|
||||||
edition: edition.isOfficial
|
edition: edition.isOfficial
|
||||||
? { id: edition.id }
|
? { id: edition.id }
|
||||||
: Object.assign({}, edition, { logo: "" }),
|
: Object.assign({}, edition, { logo: "" }),
|
||||||
|
@ -462,41 +488,37 @@ class LiveSession {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a ping message by another player / storyteller
|
* Handle a ping message by another player / storyteller
|
||||||
* @param isSpectator
|
* @param playerIdOrCount
|
||||||
* @param playerId
|
* @param latency
|
||||||
* @param timestamp
|
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_handlePing([isSpectator, playerId, latency] = []) {
|
_handlePing([playerIdOrCount = 0, latency] = []) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
// remove players that haven't sent a ping in twice the timespan
|
if (!this._isSpectator) {
|
||||||
for (let player in this._players) {
|
// remove players that haven't sent a ping in twice the timespan
|
||||||
if (now - this._players[player] > this._pingInterval * 2) {
|
for (let player in this._players) {
|
||||||
delete this._players[player];
|
if (now - this._players[player] > this._pingInterval * 2) {
|
||||||
delete this._pings[player];
|
delete this._players[player];
|
||||||
|
delete this._pings[player];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// remove claimed seats from players that are no longer connected
|
||||||
// remove claimed seats from players that are no longer connected
|
this._store.state.players.players.forEach(player => {
|
||||||
this._store.state.players.players.forEach(player => {
|
if (player.id && !this._players[player.id]) {
|
||||||
if (!this._isSpectator && player.id && !this._players[player.id]) {
|
this._store.commit("players/update", {
|
||||||
this._store.commit("players/update", {
|
player,
|
||||||
player,
|
property: "id",
|
||||||
property: "id",
|
value: ""
|
||||||
value: ""
|
});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
});
|
// store new player data
|
||||||
// store new player data
|
if (playerIdOrCount) {
|
||||||
if (playerId) {
|
this._players[playerIdOrCount] = now;
|
||||||
this._players[playerId] = now;
|
const ping = parseInt(latency, 10);
|
||||||
const ping = parseInt(latency, 10);
|
if (ping && ping > 0 && ping < 30 * 1000) {
|
||||||
if (ping && ping > 0 && ping < 30 * 1000) {
|
|
||||||
if (this._isSpectator && !isSpectator) {
|
|
||||||
// ping to ST
|
|
||||||
this._store.commit("session/setPing", ping);
|
|
||||||
} else if (!this._isSpectator) {
|
|
||||||
// ping to Players
|
// ping to Players
|
||||||
this._pings[playerId] = ping;
|
this._pings[playerIdOrCount] = ping;
|
||||||
const pings = Object.values(this._pings);
|
const pings = Object.values(this._pings);
|
||||||
this._store.commit(
|
this._store.commit(
|
||||||
"session/setPing",
|
"session/setPing",
|
||||||
|
@ -504,19 +526,26 @@ class LiveSession {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (latency) {
|
||||||
|
// ping to ST
|
||||||
|
this._store.commit("session/setPing", parseInt(latency, 10));
|
||||||
|
}
|
||||||
|
// update player count
|
||||||
|
if (!this._isSpectator || playerIdOrCount) {
|
||||||
|
this._store.commit(
|
||||||
|
"session/setPlayerCount",
|
||||||
|
this._isSpectator ? playerIdOrCount : Object.keys(this._players).length
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this._store.commit(
|
|
||||||
"session/setPlayerCount",
|
|
||||||
Object.keys(this._players).length
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a player leaving the sessions
|
* Handle a player leaving the sessions. ST only
|
||||||
* @param playerId
|
* @param playerId
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_handleBye(playerId) {
|
_handleBye(playerId) {
|
||||||
|
if (this._isSpectator) return;
|
||||||
delete this._players[playerId];
|
delete this._players[playerId];
|
||||||
this._store.commit(
|
this._store.commit(
|
||||||
"session/setPlayerCount",
|
"session/setPlayerCount",
|
||||||
|
@ -783,7 +812,7 @@ export default store => {
|
||||||
case "players/clear":
|
case "players/clear":
|
||||||
case "players/remove":
|
case "players/remove":
|
||||||
case "players/add":
|
case "players/add":
|
||||||
session.sendGamestate(true);
|
session.sendGamestate("", true);
|
||||||
break;
|
break;
|
||||||
case "players/update":
|
case "players/update":
|
||||||
session.sendPlayer(payload);
|
session.sendPlayer(payload);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
// if the app is supposed to run on Github Pages in a subfolder, use the following config:
|
||||||
|
// publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/"
|
||||||
publicPath: process.env.NODE_ENV === "production" ? "/" : "/"
|
publicPath: process.env.NODE_ENV === "production" ? "/" : "/"
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue