+ case "deletechallenge_s":
+ case "newgame":
+ case "resign":
+ case "abort":
+ case "drawoffer":
+ case "rematchoffer":
+ case "draw":
+ // "newgame" message can provide a page (corr Game --> Hall)
+ notifyRoom(
+ obj.page || page, obj.code, {data: obj.data}, obj.excluded);
+ break;
+
+ case "rnewgame":
+ // A rematch game started:
+ notifyRoom(page, "newgame", {data: obj.data});
+ // Explicitely notify Hall if gametype == corr.
+ // Live games will be polled from Hall after gconnect event.
+ if (obj.data.cadence.indexOf('d') >= 0)
+ notifyRoom("/", "newgame", {data: obj.data});
+ break;
+
+ case "newmove": {
+ const dataWithFrom = { from: [sid,tmpId], data: obj.data };
+ // Special case re-send newmove only to opponent:
+ if (!!obj.target && !!clients[page][obj.target]) {
+ Object.keys(clients[page][obj.target]).forEach(x => {
+ send(
+ clients[page][obj.target][x].socket,
+ Object.assign({ code: "newmove" }, dataWithFrom)
+ );
+ });
+ } else {
+ // NOTE: data.from is useful only to opponent
+ notifyRoom(page, "newmove", dataWithFrom);
+ }
+ break;
+ }
+ case "gotmove":
+ if (
+ !!clients[page][obj.target[0]] &&
+ !!clients[page][obj.target[0]][obj.target[1]]
+ ) {
+ send(
+ clients[page][obj.target[0]][obj.target[1]].socket,
+ { code: "gotmove" }
+ );
+ }
+ break;
+
+ case "result":
+ // Special case: notify all, 'transroom': Game --> Hall
+ notifyRoom("/", "result", { gid: obj.gid, score: obj.score });
+ break;
+
+ case "mabort": {
+ const gamePg = "/game/" + obj.gid;
+ if (!!clients[gamePg] && !!clients[gamePg][obj.target]) {
+ Object.keys(clients[gamePg][obj.target]).forEach(x => {
+ send(
+ clients[gamePg][obj.target][x].socket,
+ { code: "abort" }
+ );
+ });
+ }
+ break;
+ }
+
+ case "notifyscore":
+ case "notifyturn":
+ case "notifynewgame":
+ if (!!clients["/mygames"]) {
+ obj.targets.forEach(t => {
+ const k = t.sid || idToSid[t.id];
+ if (!!clients["/mygames"][k]) {
+ Object.keys(clients["/mygames"][k]).forEach(x => {
+ send(
+ clients["/mygames"][k][x].socket,
+ { code: obj.code, data: obj.data }
+ );
+ });
+ }
+ });
+ }
+ break;
+
+ case "getfocus":
+ case "losefocus":
+ if (
+ !!clients[page] &&
+ !!clients[page][sid] &&
+ !!clients[page][sid][tmpId]
+ ) {
+ clients[page][sid][tmpId].focus = (obj.code == "getfocus");
+ }
+ if (page == "/") notifyRoom("/", obj.code, { page: "/" }, [sid]);
+ else {
+ // Notify game room + Hall:
+ notifyRoom(page, obj.code, {}, [sid]);
+ notifyRoom("/", obj.code, { page: page }, [sid]);
+ }
+ break;
+
+ // Passing, relaying something: from isn't needed,
+ // but target is fully identified (sid + tmpId)
+ case "challenges":
+ case "fullgame":
+ case "game":
+ case "identity":
+ case "lastate":
+ {
+ const pg = obj.target[2] || page; //required for identity and game
+ // NOTE: if in game we ask identity to opponent still in Hall, but
+ // leaving Hall, clients[pg] or clients[pg][target] could be undef.
+ if (!!clients[pg] && !!clients[pg][obj.target[0]]) {
+ send(
+ clients[pg][obj.target[0]][obj.target[1]].socket,
+ { code:obj.code, data:obj.data }
+ );
+ }
+ break;
+ }
+ }
+ };
+ const closeListener = () => {
+ // For browser or tab closing (including page reload):
+ doDisconnect();
+ };
+ // Update clients object: add new connexion
+ const newElt = { socket: socket, focus: true };
+ if (!clients[page])
+ clients[page] = { [sid]: {[tmpId]: newElt } };
+ else if (!clients[page][sid])
+ clients[page][sid] = { [tmpId]: newElt };
+ else
+ clients[page][sid][tmpId] = newElt;
+ // Also update helper correspondances
+ if (!idToSid[id]) idToSid[id] = sid;
+ if (!sidToPages[sid]) sidToPages[sid] = [];
+ const pgIndex = sidToPages[sid].findIndex(pg => pg == page);
+ if (pgIndex === -1) sidToPages[sid].push(page);
+ socket.on("message", messageListener);
+ socket.on("close", closeListener);
+ });