+ case "duplicate":
+ alert(this.st.tr["Warning: multi-tabs not supported"]);
+ break;
+ // 0.2] Receive clients list (just socket IDs)
+ case "pollclients":
+ 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}));
+ });
+ break;
+ case "pollgamers":
+ // NOTE: we could make a difference between people in hall
+ // 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}));
+ });
+ // Also ask current games to all playing peers (TODO: some design issue)
+ this.st.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",
+ user: {
+ // NOTE: decompose to avoid revealing email
+ name: this.st.user.name,
+ sid: this.st.user.sid,
+ id: this.st.user.id,
+ },
+ target:data.from}));
+ }
+ break;
+ }
+ case "identity":
+ {
+ this.$set(this.people, data.user.sid,
+ {
+ id: data.user.id,
+ name: data.user.name,
+ gamer: this.people[data.user.sid].gamer,
+ });
+ break;
+ }
+ case "askchallenge":
+ {
+ // Send my current live challenge (if any)
+ const cIdx = this.challenges.findIndex(c =>
+ c.from.sid == this.st.user.sid && c.type == "live");
+ if (cIdx >= 0)
+ {
+ const c = this.challenges[cIdx];
+ // TODO: code below requires "c.to" to have given his identity,
+ // but it can happen that the identity arrives later, which
+ // prevent him from receiving the challenge.
+ // ==> Filter later (when receiving challenge)
+// if (!!c.to)
+// {
+// // Only share targeted challenges to the targets:
+// const toSid = Object.keys(this.people).find(k =>
+// this.people[k].name == c.to);
+// if (toSid != data.from)
+// return;
+// }
+ const myChallenge =
+ {
+ // Minimal challenge informations: (from not required)
+ id: c.id,
+ to: c.to,
+ fen: c.fen,
+ vid: c.vid,
+ timeControl: c.timeControl,
+ added: c.added,
+ };
+ this.st.conn.send(JSON.stringify({code:"challenge",
+ chall:myChallenge, target:data.from}));
+ }
+ break;
+ }
+ case "challenge":
+ {
+ // Receive challenge from some player (+sid)
+ // NOTE about next condition: see "askchallenge" case.
+ if (!data.chall.to || data.chall.to == this.st.user.name)
+ {
+ let newChall = data.chall;
+ newChall.type = this.classifyObject(data.chall);
+ newChall.from =
+ Object.assign({sid:data.from}, this.people[data.from]);
+ newChall.vname = this.getVname(newChall.vid);
+ this.challenges.push(newChall);
+ }
+ break;
+ }
+ case "game":
+ {
+ // Receive game from some player (+sid)
+ // NOTE: it may be correspondance (if newgame while we are connected)
+ // If duplicate found: select rid (remote ID) at random
+ let game = this.games.find(g => g.id == data.game.id);
+ if (!!game)
+ {
+ if (Math.random() < 0.5)
+ game.rid = data.from;
+ }
+ else
+ {
+ let newGame = data.game;
+ newGame.type = this.classifyObject(data.game);
+ newGame.vname = this.getVname(data.game.vid);
+ newGame.rid = data.from;
+ if (!data.game.score)
+ newGame.score = "*";
+ this.games.push(newGame);
+ }
+ break;
+ }
+ case "newgame":
+ {
+ // New game just started: data contain all information
+ if (this.classifyObject(data.gameInfo) == "live")
+ this.startNewGame(data.gameInfo);
+ else
+ {
+ this.infoMessage = "New game started: " +
+ "<a href='#/game/" + data.gameInfo.id + "'>" +
+ "#/game/" + data.gameInfo.id + "</a>";
+ let modalBox = document.getElementById("modalInfo");
+ modalBox.checked = true;
+ setTimeout(() => { modalBox.checked = false; }, 3000);
+ }
+ break;
+ }
+ case "newchat":
+ this.newChat = data.chat;
+ break;
+ case "refusechallenge":
+ {
+ ArrayFun.remove(this.challenges, c => c.id == data.cid);
+ localStorage.removeItem("challenge");
+ alert(this.st.tr["Challenge declined"]);
+ break;
+ }
+ case "deletechallenge":
+ {
+ // NOTE: the challenge may be already removed
+ ArrayFun.remove(this.challenges, c => c.id == data.cid);
+ localStorage.removeItem("challenge"); //in case of
+ break;
+ }
+ 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}));
+ if (data.code == "connect")
+ this.st.conn.send(JSON.stringify({code:"askchallenge", target:data.from}));
+ else
+ this.st.conn.send(JSON.stringify({code:"askgame", target:data.from}));
+ break;
+ case "disconnect":
+ case "gdisconnect":
+ this.$delete(this.people, data.from);
+ if (data.code == "disconnect")
+ {
+ // Also remove all challenges sent by this player:
+ ArrayFun.remove(this.challenges, c => c.from.sid == data.from);
+ }
+ else
+ {
+ // And all live games where he plays and no other opponent is online
+ ArrayFun.remove(this.games, g =>
+ g.type == "live" && (g.players.every(p => p.sid == data.from
+ || !this.people[p.sid])), "all");
+ }
+ break;
+ }
+ },
+ // Challenge lifecycle:
+ tryChallenge: function(sid) {
+ if (this.people[sid].id == 0)
+ return; //anonymous players cannot be challenged
+ // TODO: SID is available, so we could use it instead of searching from name
+ this.newchallenge.to = this.people[sid].name;
+ doClick("modalNewgame");
+ },
+ challOrWatch: function(sid) {
+ if (!this.people[sid].gamer)
+ {
+ // Available, in Hall
+ this.tryChallenge(sid);
+ }
+ else
+ {
+ // Playing, in Game
+ this.showGame(this.games.find(
+ g => g.players.some(pl => pl.sid == sid || pl.uid == this.people[sid].id)));