From: Benjamin Auder Date: Mon, 10 Feb 2020 18:16:41 +0000 (+0100) Subject: On the way to multi-tabs support X-Git-Url: https://git.auder.net/assets/bundles/framework/images/css/mini-custom.min.css?a=commitdiff_plain;h=8418f0d79395f40172b11d62eef8b83112f1d240;p=vchess.git On the way to multi-tabs support --- diff --git a/client/src/main.js b/client/src/main.js index 2d855b8a..06b050b7 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -22,7 +22,6 @@ new Vue({ }); } }); - // NOTE: store.initialize(this.$route.path); doesn't work - store.initialize(window.location.href.split("#")[1].split("?")[0]); + store.initialize(); }, }).$mount("#app"); diff --git a/client/src/router.js b/client/src/router.js index daa9619d..b2efee78 100644 --- a/client/src/router.js +++ b/client/src/router.js @@ -61,14 +61,4 @@ const router = new Router({ ] }); -router.beforeEach((to, from, next) => { - window.scrollTo(0, 0); - if (!!store.state.conn) //uninitialized at first page - { - // Notify WebSockets server (TODO: path or fullPath?) - store.state.conn.send(JSON.stringify({code: "pagechange", page: to.path})); - } - next(); -}); - export default router; diff --git a/client/src/store.js b/client/src/store.js index a1e432f3..bcee338e 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -1,6 +1,5 @@ import { ajax } from "./utils/ajax"; import { getRandString } from "./utils/alea"; -import params from "./parameters"; //for socket connection // Global store: see https://medium.com/fullstackio/managing-state-in-vue-js-23a0352b1c87 export const store = @@ -9,12 +8,11 @@ export const store = variants: [], tr: {}, user: {}, - conn: null, settings: {}, lang: "", }, socketCloseListener: null, - initialize(page) { + initialize() { ajax("/variants", "GET", res => { this.state.variants = res.variantArray; }); let mysid = localStorage["mysid"]; if (!mysid) @@ -38,15 +36,6 @@ export const store = this.state.user.email = res.email; this.state.user.notify = res.notify; }); - const supportedLangs = ["en","es","fr"]; - this.state.lang = localStorage["lang"] || - (supportedLangs.includes(navigator.language) - ? navigator.language - : "en"); - this.setTranslations(); - // Initialize connection (even if the current page doesn't need it) - this.state.conn = new WebSocket(params.socketUrl + "/?sid=" + mysid + - "&page=" + encodeURIComponent(page)); // Settings initialized with values from localStorage this.state.settings = { bcolor: localStorage.getItem("bcolor") || "lichess", @@ -54,12 +43,12 @@ export const store = hints: localStorage.getItem("hints") == "true", highlight: localStorage.getItem("highlight") == "true", }; - this.socketCloseListener = () => { - // Next line may fail at first, but should retry and eventually success (TODO?) - this.state.conn = new WebSocket(params.socketUrl + "/?sid=" + mysid + - "&page=" + encodeURIComponent(page)); - }; - this.state.conn.onclose = this.socketCloseListener; + const supportedLangs = ["en","es","fr"]; + this.state.lang = localStorage["lang"] || + (supportedLangs.includes(navigator.language) + ? navigator.language + : "en"); + this.setTranslations(); }, updateSetting: function(propName, value) { this.state.settings[propName] = value; diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 022e3cb6..177f9eb3 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -44,6 +44,7 @@ import { extractTime } from "@/utils/timeControl"; import { ArrayFun } from "@/utils/array"; import { processModalClick } from "@/utils/modalClick"; import { getScoreMessage } from "@/utils/scoring"; +import params from "@/parameters"; export default { name: 'my-game', @@ -70,6 +71,9 @@ export default { lastate: undefined, //used if opponent send lastate before game is ready repeat: {}, //detect position repetition newChat: "", + conn: null, + page: "", + tempId: "", //to distinguish several tabs }; }, watch: { @@ -114,20 +118,26 @@ export default { this.$set(this.people, my.sid, {id:my.id, name:my.name}); this.gameRef.id = this.$route.params["id"]; this.gameRef.rid = this.$route.query["rid"]; //may be undefined - // Define socket .onmessage() and .onclose() events: - this.st.conn.onmessage = this.socketMessageListener; + // Initialize connection + this.page = this.$route.path; + const connexionString = params.socketUrl + + "/?sid=" + this.st.user.sid + + "&tmpId=" + this.tempId + + "&page=" + encodeURIComponent(this.page); + this.conn = new WebSocket(connexionString); + this.conn.onmessage = this.socketMessageListener; const socketCloseListener = () => { - store.socketCloseListener(); //reinitialize connexion (in store.js) - this.st.conn.addEventListener('message', this.socketMessageListener); - this.st.conn.addEventListener('close', socketCloseListener); + this.conn = new WebSocket(connexionString); + this.conn.addEventListener('message', this.socketMessageListener); + this.conn.addEventListener('close', socketCloseListener); }; - this.st.conn.onclose = socketCloseListener; + this.conn.onclose = socketCloseListener; // Socket init required before loading remote game: const socketInit = (callback) => { - if (!!this.st.conn && this.st.conn.readyState == 1) //1 == OPEN state + if (!!this.conn && this.conn.readyState == 1) //1 == OPEN state callback(); else //socket not ready yet (initial loading) - this.st.conn.onopen = callback; + this.conn.onopen = callback; }; if (!this.gameRef.rid) //game stored locally or on server this.loadGame(null, () => socketInit(this.roomInit)); @@ -143,13 +153,16 @@ export default { document.getElementById("chatWrap").addEventListener( "click", processModalClick); }, + beforeDestroy: function() { + this.conn.send(JSON.stringify({code:"disconnect",page:this.page})); + }, methods: { // O.1] Ask server for room composition: roomInit: function() { // Notify the room only now that I connected, because // messages might be lost otherwise (if game loading is slow) - this.st.conn.send(JSON.stringify({code:"connect"})); - this.st.conn.send(JSON.stringify({code:"pollclients"})); + this.conn.send(JSON.stringify({code:"connect"})); + this.conn.send(JSON.stringify({code:"pollclients"})); }, isConnected: function(index) { const player = this.game.players[index]; @@ -165,7 +178,7 @@ export default { switch (data.code) { case "duplicate": - this.st.conn.send(JSON.stringify({code:"duplicate", + this.conn.send(JSON.stringify({code:"duplicate", page:"/game/" + this.game.id})); alert(this.st.tr["This tab is now offline"]); break; @@ -176,14 +189,14 @@ export default { return; this.$set(this.people, sid, {id:0, name:""}); // Ask only identity - this.st.conn.send(JSON.stringify({code:"askidentity", target:sid})); + this.conn.send(JSON.stringify({code:"askidentity", target:sid})); }); break; case "askidentity": // Request for identification: reply if I'm not anonymous if (this.st.user.id > 0) { - this.st.conn.send(JSON.stringify({code:"identity", + this.conn.send(JSON.stringify({code:"identity", user: { // NOTE: decompose to avoid revealing email name: this.st.user.name, @@ -201,7 +214,7 @@ export default { && this.game.type == "live" && this.game.score == "*" && this.game.players.some(p => p.sid == data.user.sid)) { - this.st.conn.send(JSON.stringify({code:"asklastate", target:data.user.sid})); + this.conn.send(JSON.stringify({code:"asklastate", target:data.user.sid})); } break; case "asklastate": @@ -212,7 +225,7 @@ export default { // Send our "last state" informations to opponent const L = this.game.moves.length; const myIdx = ["w","b"].indexOf(this.game.mycolor); - this.st.conn.send(JSON.stringify({ + this.conn.send(JSON.stringify({ code: "lastate", target: data.from, state: @@ -245,7 +258,7 @@ export default { timeControl: this.game.timeControl, score: this.game.score, }; - this.st.conn.send(JSON.stringify({code:"game", + this.conn.send(JSON.stringify({code:"game", game:myGame, target:data.from})); } break; @@ -284,7 +297,7 @@ export default { this.drawOffer = "received"; break; case "askfullgame": - this.st.conn.send(JSON.stringify({code:"fullgame", + this.conn.send(JSON.stringify({code:"fullgame", game:this.game, target:data.from})); break; case "fullgame": @@ -293,7 +306,7 @@ export default { break; case "connect": this.$set(this.people, data.from, {name:"", id:0}); - this.st.conn.send(JSON.stringify({code:"askidentity", target:data.from})); + this.conn.send(JSON.stringify({code:"askidentity", target:data.from})); break; case "disconnect": this.$delete(this.people, data.from); @@ -332,7 +345,7 @@ export default { Object.keys(this.people).forEach(sid => { if (sid != this.st.user.sid) { - this.st.conn.send(JSON.stringify({code:"draw", + this.conn.send(JSON.stringify({code:"draw", message:message, target:sid})); } }); @@ -347,7 +360,7 @@ export default { this.drawOffer = "sent"; Object.keys(this.people).forEach(sid => { if (sid != this.st.user.sid) - this.st.conn.send(JSON.stringify({code:"drawoffer", target:sid})); + this.conn.send(JSON.stringify({code:"drawoffer", target:sid})); }); GameStorage.update(this.gameRef.id, {drawOffer: this.game.mycolor}); } @@ -359,7 +372,7 @@ export default { Object.keys(this.people).forEach(sid => { if (sid != this.st.user.sid) { - this.st.conn.send(JSON.stringify({ + this.conn.send(JSON.stringify({ code: "abort", target: sid, })); @@ -372,7 +385,7 @@ export default { Object.keys(this.people).forEach(sid => { if (sid != this.st.user.sid) { - this.st.conn.send(JSON.stringify({code:"resign", + this.conn.send(JSON.stringify({code:"resign", side:this.game.mycolor, target:sid})); } }); @@ -514,7 +527,7 @@ export default { if (!!this.gameRef.rid) { // Remote live game: forgetting about callback func... (TODO: design) - this.st.conn.send(JSON.stringify( + this.conn.send(JSON.stringify( {code:"askfullgame", target:this.gameRef.rid})); } else @@ -561,7 +574,7 @@ export default { Object.keys(this.people).forEach(sid => { if (sid != this.st.user.sid) { - this.st.conn.send(JSON.stringify({ + this.conn.send(JSON.stringify({ code: "newmove", target: sid, move: sendMove, @@ -641,7 +654,7 @@ export default { document.getElementById("chatBtn").style.backgroundColor = "#e2e2e2"; }, processChat: function(chat) { - this.st.conn.send(JSON.stringify({code:"newchat", chat:chat})); + this.conn.send(JSON.stringify({code:"newchat", chat:chat})); // NOTE: anonymous chats in corr games are not stored on server (TODO?) if (this.game.type == "corr" && this.st.user.id > 0) GameStorage.update(this.gameRef.id, {chat: chat}); diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 2395f60e..e53dd56a 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -78,6 +78,7 @@ import { store } from "@/store"; import { checkChallenge } from "@/data/challengeCheck"; import { ArrayFun } from "@/utils/array"; import { ajax } from "@/utils/ajax"; +import params from "@/parameters"; import { getRandString, shuffle } from "@/utils/alea"; import Chat from "@/components/Chat.vue"; import GameList from "@/components/GameList.vue"; @@ -109,6 +110,9 @@ export default { timeControl: localStorage.getItem("timeControl") || "", }, newChat: "", + conn: null, + page: "", + tempId: "", //to distinguish several tabs }; }, watch: { @@ -135,7 +139,8 @@ export default { created: function() { // Always add myself to players' list const my = this.st.user; - this.$set(this.people, my.sid, {id:my.id, name:my.name}); + this.tempId = getRandString(); + this.$set(this.people, my.sid, {id:my.id, name:my.name, tmpId: [this.tempId]}); // Ask server for current corr games (all but mines) ajax( "/games", @@ -199,23 +204,25 @@ export default { ); // 0.1] Ask server for room composition: const funcPollClients = () => { - // Same strategy as in Game.vue: send connection - // after we're sure WebSocket is initialized - this.st.conn.send(JSON.stringify({code:"connect"})); - this.st.conn.send(JSON.stringify({code:"pollclients"})); - this.st.conn.send(JSON.stringify({code:"pollgamers"})); + this.conn.send(JSON.stringify({code:"connect"})); + this.conn.send(JSON.stringify({code:"pollclients"})); + this.conn.send(JSON.stringify({code:"pollgamers"})); }; - if (!!this.st.conn && this.st.conn.readyState == 1) //1 == OPEN state - funcPollClients(); - else //socket not ready yet (initial loading) - this.st.conn.onopen = funcPollClients; - this.st.conn.onmessage = this.socketMessageListener; + // Initialize connection + this.page = this.$route.path; + const connexionString = params.socketUrl + + "/?sid=" + this.st.user.sid + + "&tmpId=" + this.tempId + + "&page=" + encodeURIComponent(this.page); + this.conn = new WebSocket(connexionString); + this.conn.onopen = funcPollClients; + this.conn.onmessage = this.socketMessageListener; const socketCloseListener = () => { - store.socketCloseListener(); //reinitialize connexion (in store.js) - this.st.conn.addEventListener('message', this.socketMessageListener); - this.st.conn.addEventListener('close', socketCloseListener); + this.conn = new WebSocket(connexionString); + this.conn.addEventListener('message', this.socketMessageListener); + this.conn.addEventListener('close', socketCloseListener); }; - this.st.conn.onclose = socketCloseListener; + this.conn.onclose = socketCloseListener; }, mounted: function() { [document.getElementById("infoDiv"),document.getElementById("newgameDiv")] @@ -226,6 +233,9 @@ export default { )} ); }, + beforeDestroy: function() { + this.conn.send(JSON.stringify({code:"disconnect",page:this.page})); + }, methods: { // Helpers: filterChallenges: function(type) { @@ -260,11 +270,11 @@ export default { }, processChat: function(chat) { // When received on server, this will trigger a "notifyRoom" - this.st.conn.send(JSON.stringify({code:"newchat", chat: chat})); + this.conn.send(JSON.stringify({code:"newchat", chat: chat})); }, sendSomethingTo: function(to, code, obj, warnDisconnected) { const doSend = (code, obj, sid) => { - this.st.conn.send(JSON.stringify(Object.assign( + this.conn.send(JSON.stringify(Object.assign( {code: code}, obj, {target: sid} @@ -307,8 +317,8 @@ export default { switch (data.code) { case "duplicate": - this.st.conn.send(JSON.stringify({code:"duplicate", page:"/"})); - this.st.conn.send = () => {}; + this.conn.send(JSON.stringify({code:"duplicate", page:"/"})); + this.conn.send = () => {}; alert(this.st.tr["This tab is now offline"]); break; // 0.2] Receive clients list (just socket IDs) @@ -316,8 +326,8 @@ export default { data.sockIds.forEach(sid => { this.$set(this.people, sid, {id:0, name:""}); // Ask identity and challenges - this.st.conn.send(JSON.stringify({code:"askidentity", target:sid})); - this.st.conn.send(JSON.stringify({code:"askchallenge", target:sid})); + this.conn.send(JSON.stringify({code:"askidentity", target:sid})); + this.conn.send(JSON.stringify({code:"askchallenge", target:sid})); }); break; case "pollgamers": @@ -325,16 +335,16 @@ export default { // and gamers, but is it necessary? data.sockIds.forEach(sid => { this.$set(this.people, sid, {id:0, name:"", gamer:true}); - this.st.conn.send(JSON.stringify({code:"askidentity", target:sid})); + this.conn.send(JSON.stringify({code:"askidentity", target:sid})); }); // Also ask current games to all playing peers (TODO: some design issue) - this.st.conn.send(JSON.stringify({code:"askgames"})); + this.conn.send(JSON.stringify({code:"askgames"})); break; case "askidentity": // Request for identification: reply if I'm not anonymous if (this.st.user.id > 0) { - this.st.conn.send(JSON.stringify({code:"identity", + this.conn.send(JSON.stringify({code:"identity", user: { // NOTE: decompose to avoid revealing email name: this.st.user.name, @@ -382,7 +392,7 @@ export default { timeControl: c.timeControl, added: c.added, }; - this.st.conn.send(JSON.stringify({code:"challenge", + this.conn.send(JSON.stringify({code:"challenge", chall:myChallenge, target:data.from})); } break; @@ -451,11 +461,11 @@ export default { case "connect": case "gconnect": this.$set(this.people, data.from, {name:"", id:0, gamer:data.code[0]=='g'}); - this.st.conn.send(JSON.stringify({code:"askidentity", target:data.from})); + this.conn.send(JSON.stringify({code:"askidentity", target:data.from})); if (data.code == "connect") - this.st.conn.send(JSON.stringify({code:"askchallenge", target:data.from})); + this.conn.send(JSON.stringify({code:"askchallenge", target:data.from})); else - this.st.conn.send(JSON.stringify({code:"askgame", target:data.from})); + this.conn.send(JSON.stringify({code:"askgame", target:data.from})); break; case "disconnect": case "gdisconnect": @@ -595,7 +605,7 @@ export default { } else { - this.st.conn.send(JSON.stringify({ + this.conn.send(JSON.stringify({ code: "refusechallenge", cid: c.id, target: c.from.sid})); } @@ -639,7 +649,7 @@ export default { const tryNotifyOpponent = () => { if (!!oppsid) //opponent is online { - this.st.conn.send(JSON.stringify({code:"newgame", + this.conn.send(JSON.stringify({code:"newgame", gameInfo:gameInfo, target:oppsid, cid:c.id})); } }; @@ -666,7 +676,7 @@ export default { Object.keys(this.people).forEach(sid => { if (![this.st.user.sid,oppsid].includes(sid)) { - this.st.conn.send(JSON.stringify({code:"game", + this.conn.send(JSON.stringify({code:"game", game: { //minimal game info: id: gameInfo.id, players: gameInfo.players, diff --git a/server/sockets.js b/server/sockets.js index 64b180db..11f7d8ec 100644 --- a/server/sockets.js +++ b/server/sockets.js @@ -14,12 +14,14 @@ function getJsonFromUrl(url) } module.exports = function(wss) { - let clients = {}; //associative array sid --> socket + // Associative array sid --> tmpId --> {socket, page}, + // "page" is either "/" for hall or "/game/some_gid" for Game, + // tmpId is required if a same user (browser) has different tabs + let clients = {}; wss.on("connection", (socket, req) => { const query = getJsonFromUrl(req.url); - if (query["page"] != "/" && query["page"].indexOf("/game/") < 0) - return; //other tabs don't need to be connected const sid = query["sid"]; + const tmpId = query["tmpId"]; const notifyRoom = (page,code,obj={},excluded=[]) => { Object.keys(clients).forEach(k => { if (k in excluded) @@ -27,7 +29,7 @@ module.exports = function(wss) { if (k != sid && clients[k].page == page) { clients[k].sock.send(JSON.stringify(Object.assign( - {code:code, from:sid}, obj))); + {code:code, from:[sid,tmpId]}, obj))); } }); }; @@ -37,93 +39,112 @@ module.exports = function(wss) { return; //receiver not connected, nothing we can do switch (obj.code) { - case "duplicate": - // Turn off message listening, and send disconnect if needed: - socket.removeListener("message", messageListener); - socket.removeListener("close", closeListener); - // From obj.page to clients[sid].page (TODO: unclear) - if (clients[sid].page != obj.page) - { - notifyRoom(obj.page, "disconnect"); - if (obj.page.indexOf("/game/") >= 0) - notifyRoom("/", "gdisconnect"); - } - break; // Wait for "connect" message to notify connection to the room, // because if game loading is slow the message listener might // not be ready too early. case "connect": { - const curPage = clients[sid].page; + const curPage = clients[sid][tmpId].page; notifyRoom(curPage, "connect"); //Hall or Game if (curPage.indexOf("/game/") >= 0) notifyRoom("/", "gconnect"); //notify main hall break; } + case "disconnect": + { + const oldPage = obj.page; + notifyRoom(oldPage, "disconnect"); //Hall or Game + if (oldPage.indexOf("/game/") >= 0) + notifyRoom("/", "gdisconnect"); //notify main hall + break; + } case "pollclients": { - const curPage = clients[sid].page; - socket.send(JSON.stringify({code:"pollclients", - sockIds: Object.keys(clients).filter(k => - k != sid && clients[k].page == curPage - )})); + const curPage = clients[sid][tmpId].page; + let sockIds = {}; //result, object sid ==> [tmpIds] + Object.keys(clients).forEach(k => { + Object.keys(clients[k]).forEach(x => { + if ((k != sid || x != tmpId) + && clients[k][x].page == curPage) + { + if (!sockIds[k]) + sockIds[k] = [x]; + else + sockIds[k].push(x); + } + }); + }); + socket.send(JSON.stringify({code:"pollclients", sockIds:sockIds})); break; } case "pollgamers": - socket.send(JSON.stringify({code:"pollgamers", - sockIds: Object.keys(clients).filter(k => - k != sid && clients[k].page.indexOf("/game/") >= 0 - )})); - break; - case "pagechange": - // page change clients[sid].page --> obj.page - // TODO: some offline rooms don't need to receive disconnect event - notifyRoom(clients[sid].page, "disconnect"); - if (clients[sid].page.indexOf("/game/") >= 0) - notifyRoom("/", "gdisconnect"); - clients[sid].page = obj.page; - // No need to notify connection: it's self-sent in .vue file - //notifyRoom(obj.page, "connect"); - if (obj.page.indexOf("/game/") >= 0) - notifyRoom("/", "gconnect"); + { + let sockIds = {}; + Object.keys(clients).forEach(k => { + Object.keys(clients[k]).forEach(x => { + if ((k != sid || x != tmpId) + && clients[k][x].page.indexOf("/game/") >= 0) + { + if (!sockIds[k]) + sockIds[k] = [x]; + else + sockIds[k].push(x); + } + }); + }); + socket.send(JSON.stringify({code:"pollgamers", sockIds:sockIds})); break; + } case "askidentity": - clients[obj.target].sock.send(JSON.stringify( - {code:"askidentity",from:sid})); + { + // Identity only depends on sid, so select a tmpId at random + const tmpIds = Object.keys(clients[obj.target]); + const tmpId_idx = Math.floor(Math.random() * tmpIds.length); + clients[obj.target][tmpIds[tmpId_idx]].sock.send(JSON.stringify( + {code:"askidentity",from:[sid,tmpId]})); break; + } case "asklastate": - clients[obj.target].sock.send(JSON.stringify( - {code:"asklastate",from:sid})); + clients[obj.target[0]][obj.target[1]].sock.send(JSON.stringify( + {code:"asklastate",from:[sid,tmpId]})); break; case "askchallenge": - clients[obj.target].sock.send(JSON.stringify( - {code:"askchallenge",from:sid})); + clients[obj.target[0]][obj.target[1]].sock.send(JSON.stringify( + {code:"askchallenge",from:[sid,tmpId]})); break; case "askgames": { // Check all clients playing, and send them a "askgame" message - let gameSids = {}; //game ID --> [sid1, sid2] + // game ID --> [ sid1 --> array of tmpIds, sid2 --> array of tmpIds] + let gameSids = {}; const regexpGid = /\/[a-zA-Z0-9]+$/; Object.keys(clients).forEach(k => { - if (k != sid && clients[k].page.indexOf("/game/") >= 0) + Object.keys(clients[k]).forEach(x => { + if ((k != sid || x != tmpId) + && clients[k][x].page.indexOf("/game/") >= 0) { - const gid = clients[k].page.match(regexpGid)[0]; + const gid = clients[k][x].page.match(regexpGid)[0]; if (!gameSids[gid]) - gameSids[gid] = [k]; + gameSids[gid] = [{k: [x]}]; + else if (k == Object.keys(gameSids[gid][0])[0]) + gameSids[gid][0][k].push(x); + else if (gameSids[gid].length == 1) + gameSids[gid].push({k: [x]}); else - gameSids[gid].push(k); + Object.values(gameSids[gid][1]).push(x); } }); // Request only one client out of 2 (TODO: this is a bit heavy) // Alt: ask game to all, and filter later? Object.keys(gameSids).forEach(gid => { const L = gameSids[gid].length; - const idx = L > 1 + const sidIdx = L > 1 ? Math.floor(Math.random() * Math.floor(L)) : 0; - const rid = gameSids[gid][idx]; - clients[rid].sock.send(JSON.stringify( - {code:"askgame", from: sid})); + const tmpIdx = Object.values(gameSids[gid][sidIdx] + const rid = gameSids[gid][sidIdx][tmpIdx]; + clients[sidIdx][tmpIdx].sock.send(JSON.stringify( + {code:"askgame", from: [sid,tmpId]})); }); break; } @@ -203,11 +224,7 @@ module.exports = function(wss) { } }; const closeListener = () => { - const page = clients[sid].page; delete clients[sid]; - notifyRoom(page, "disconnect"); - if (page.indexOf("/game/") >= 0) - notifyRoom("/", "gdisconnect"); //notify main hall }; if (!!clients[sid]) {