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)
|
||||
.split(",", 1)
|
||||
.pop();
|
||||
// don't log ping messages
|
||||
if (messageType !== '"ping"') {
|
||||
console.log(new Date(), wss.clients.size, ws.channel, ws.playerId, data);
|
||||
}
|
||||
// handle "direct" messages differently
|
||||
if (messageType === '"direct"') {
|
||||
try {
|
||||
const dataToPlayer = JSON.parse(data)[1];
|
||||
switch (messageType) {
|
||||
case '"ping"':
|
||||
// ping messages will only be sent host -> all or all -> host
|
||||
channels[ws.channel].forEach(function each(client) {
|
||||
if (
|
||||
client !== ws &&
|
||||
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();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("error parsing direct message JSON", e);
|
||||
}
|
||||
} else {
|
||||
// all other messages
|
||||
channels[ws.channel].forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
// inject latency between both clients if ping message
|
||||
if (messageType === '"ping"' && client.latency && ws.latency) {
|
||||
client.send(data.replace(/latency/, client.latency + ws.latency));
|
||||
} else {
|
||||
client.send(data);
|
||||
}
|
||||
metrics.messages_outgoing.inc();
|
||||
break;
|
||||
case '"direct"':
|
||||
// handle "direct" messages differently
|
||||
console.log(
|
||||
new Date(),
|
||||
wss.clients.size,
|
||||
ws.channel,
|
||||
ws.playerId,
|
||||
data
|
||||
);
|
||||
try {
|
||||
const dataToPlayer = JSON.parse(data)[1];
|
||||
channels[ws.channel].forEach(function each(client) {
|
||||
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.
|
||||
* @private
|
||||
*/
|
||||
_onOpen() {
|
||||
if (this._isSpectator) {
|
||||
this._send("req", "gs");
|
||||
this._sendDirect(
|
||||
"host",
|
||||
"getGamestate",
|
||||
this._store.state.session.playerId
|
||||
);
|
||||
} else {
|
||||
this.sendGamestate();
|
||||
}
|
||||
|
@ -80,12 +100,13 @@ class LiveSession {
|
|||
* @private
|
||||
*/
|
||||
_ping() {
|
||||
this._handlePing();
|
||||
this._send("ping", [
|
||||
this._isSpectator,
|
||||
this._store.state.session.playerId,
|
||||
this._isSpectator
|
||||
? this._store.state.session.playerId
|
||||
: Object.keys(this._players).length,
|
||||
"latency"
|
||||
]);
|
||||
this._handlePing();
|
||||
clearTimeout(this._pingTimer);
|
||||
this._pingTimer = setTimeout(this._ping.bind(this), this._pingInterval);
|
||||
}
|
||||
|
@ -103,10 +124,8 @@ class LiveSession {
|
|||
console.log("unsupported socket message", data);
|
||||
}
|
||||
switch (command) {
|
||||
case "req":
|
||||
if (params === "gs") {
|
||||
this.sendGamestate();
|
||||
}
|
||||
case "getGamestate":
|
||||
this.sendGamestate(params);
|
||||
break;
|
||||
case "edition":
|
||||
this._updateEdition(params);
|
||||
|
@ -204,7 +223,9 @@ class LiveSession {
|
|||
this._store.commit("session/setReconnecting", false);
|
||||
clearTimeout(this._reconnectTimer);
|
||||
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 = null;
|
||||
}
|
||||
|
@ -213,9 +234,10 @@ class LiveSession {
|
|||
/**
|
||||
* Publish the current gamestate.
|
||||
* Optional param to reduce traffic. (send only player data)
|
||||
* @param playerId
|
||||
* @param isLightweight
|
||||
*/
|
||||
sendGamestate(isLightweight = false) {
|
||||
sendGamestate(playerId = "", isLightweight = false) {
|
||||
if (this._isSpectator) return;
|
||||
this._gamestate = this._store.state.players.players.map(player => ({
|
||||
name: player.name,
|
||||
|
@ -227,12 +249,15 @@ class LiveSession {
|
|||
: {})
|
||||
}));
|
||||
if (isLightweight) {
|
||||
this._send("gs", { gamestate: this._gamestate, isLightweight });
|
||||
this._sendDirect(playerId, "gs", {
|
||||
gamestate: this._gamestate,
|
||||
isLightweight
|
||||
});
|
||||
} else {
|
||||
const { session, grimoire } = this._store.state;
|
||||
const { fabled } = this._store.state.players;
|
||||
this.sendEdition();
|
||||
this._send("gs", {
|
||||
this.sendEdition(playerId);
|
||||
this._sendDirect(playerId, "gs", {
|
||||
gamestate: this._gamestate,
|
||||
isNight: grimoire.isNight,
|
||||
nomination: session.nomination,
|
||||
|
@ -322,15 +347,16 @@ class LiveSession {
|
|||
|
||||
/**
|
||||
* Publish an edition update. ST only
|
||||
* @param playerId
|
||||
*/
|
||||
sendEdition() {
|
||||
sendEdition(playerId = "") {
|
||||
if (this._isSpectator) return;
|
||||
const { edition } = this._store.state;
|
||||
let roles;
|
||||
if (!edition.isOfficial) {
|
||||
roles = Array.from(this._store.state.roles.keys());
|
||||
}
|
||||
this._send("edition", {
|
||||
this._sendDirect(playerId, "edition", {
|
||||
edition: edition.isOfficial
|
||||
? { id: edition.id }
|
||||
: Object.assign({}, edition, { logo: "" }),
|
||||
|
@ -462,41 +488,37 @@ class LiveSession {
|
|||
|
||||
/**
|
||||
* Handle a ping message by another player / storyteller
|
||||
* @param isSpectator
|
||||
* @param playerId
|
||||
* @param timestamp
|
||||
* @param playerIdOrCount
|
||||
* @param latency
|
||||
* @private
|
||||
*/
|
||||
_handlePing([isSpectator, playerId, latency] = []) {
|
||||
_handlePing([playerIdOrCount = 0, latency] = []) {
|
||||
const now = new Date().getTime();
|
||||
// remove players that haven't sent a ping in twice the timespan
|
||||
for (let player in this._players) {
|
||||
if (now - this._players[player] > this._pingInterval * 2) {
|
||||
delete this._players[player];
|
||||
delete this._pings[player];
|
||||
if (!this._isSpectator) {
|
||||
// remove players that haven't sent a ping in twice the timespan
|
||||
for (let player in this._players) {
|
||||
if (now - this._players[player] > this._pingInterval * 2) {
|
||||
delete this._players[player];
|
||||
delete this._pings[player];
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove claimed seats from players that are no longer connected
|
||||
this._store.state.players.players.forEach(player => {
|
||||
if (!this._isSpectator && player.id && !this._players[player.id]) {
|
||||
this._store.commit("players/update", {
|
||||
player,
|
||||
property: "id",
|
||||
value: ""
|
||||
});
|
||||
}
|
||||
});
|
||||
// store new player data
|
||||
if (playerId) {
|
||||
this._players[playerId] = now;
|
||||
const ping = parseInt(latency, 10);
|
||||
if (ping && ping > 0 && ping < 30 * 1000) {
|
||||
if (this._isSpectator && !isSpectator) {
|
||||
// ping to ST
|
||||
this._store.commit("session/setPing", ping);
|
||||
} else if (!this._isSpectator) {
|
||||
// remove claimed seats from players that are no longer connected
|
||||
this._store.state.players.players.forEach(player => {
|
||||
if (player.id && !this._players[player.id]) {
|
||||
this._store.commit("players/update", {
|
||||
player,
|
||||
property: "id",
|
||||
value: ""
|
||||
});
|
||||
}
|
||||
});
|
||||
// store new player data
|
||||
if (playerIdOrCount) {
|
||||
this._players[playerIdOrCount] = now;
|
||||
const ping = parseInt(latency, 10);
|
||||
if (ping && ping > 0 && ping < 30 * 1000) {
|
||||
// ping to Players
|
||||
this._pings[playerId] = ping;
|
||||
this._pings[playerIdOrCount] = ping;
|
||||
const pings = Object.values(this._pings);
|
||||
this._store.commit(
|
||||
"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
|
||||
* @private
|
||||
*/
|
||||
_handleBye(playerId) {
|
||||
if (this._isSpectator) return;
|
||||
delete this._players[playerId];
|
||||
this._store.commit(
|
||||
"session/setPlayerCount",
|
||||
|
@ -783,7 +812,7 @@ export default store => {
|
|||
case "players/clear":
|
||||
case "players/remove":
|
||||
case "players/add":
|
||||
session.sendGamestate(true);
|
||||
session.sendGamestate("", true);
|
||||
break;
|
||||
case "players/update":
|
||||
session.sendPlayer(payload);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
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" ? "/" : "/"
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue