+ // Messaging center:
+ socketMessageListener: function(msg) {
+ const data = JSON.parse(msg.data);
+ switch (data.code)
+ {
+ case "duplicate":
+ alert("Warning: duplicate 'offline' connection");
+ 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, challenges and game(s)
+ this.st.conn.send(JSON.stringify({code:"askidentity", target:sid}));
+ this.st.conn.send(JSON.stringify({code:"askchallenge", 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});
+ 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];
+ const myChallenge =
+ {
+ // Minimal challenge informations: (from not required)
+ id: c.id,
+ to: c.to,
+ fen: c.fen,
+ vid: c.vid,
+ timeControl: c.timeControl
+ };
+ this.st.conn.send(JSON.stringify({code:"challenge",
+ chall:myChallenge, target:data.from}));
+ }
+ break;
+ }
+ case "challenge":
+ {
+ // Receive challenge from some player (+sid)
+ let newChall = data.chall;
+ newChall.type = this.classifyObject(data.chall);
+ newChall.from =
+ Object.assign({sid:data.from}, this.people[data.from]);
+ newChall.added = Date.now(); //TODO: this is reception timestamp, not creation
+ 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 (this.games.every(g => g.id != data.game.id)) //ignore duplicates
+ {
+ let newGame = data.game;
+ newGame.type = this.classifyObject(data.game);
+ newGame.vname = this.getVname(data.game.vid);
+ newGame.rid = data.from;
+ newGame.score = "*";
+ this.games.push(newGame);
+ }
+ break;
+ }
+ case "newgame":
+ {
+ // TODO: next line required ?!
+ //ArrayFun.remove(this.challenges, c => c.id == data.cid);
+ // 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 "refusechallenge":
+ {
+ alert(this.people[data.from].name + " declined your challenge");
+ ArrayFun.remove(this.challenges, c => c.id == data.cid);
+ 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":
+ {
+ this.$set(this.people, data.from, {name:"", id:0});
+ this.st.conn.send(JSON.stringify({code:"askidentity", target:data.from}));
+ this.st.conn.send(JSON.stringify({code:"askchallenge", target:data.from}));
+ this.st.conn.send(JSON.stringify({code:"askgame", target:data.from}));
+ break;
+ }
+ case "disconnect":
+ {
+ this.$delete(this.people, data.from);
+ // Also remove all challenges sent by this player:
+ ArrayFun.remove(this.challenges, c => c.from.sid == data.from);
+ // 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(player) {
+ if (player.id == 0)
+ return; //anonymous players cannot be challenged
+ this.newchallenge.to = player.name;
+ doClick("modalNewgame");
+ },
+ challOrWatch: function(p, e) {
+ switch (e.target.innerHTML)
+ {
+ case "Challenge":
+ this.tryChallenge(p);
+ break;
+ case "Playing":
+ // NOTE: this search for game was already done for rendering
+ this.showGame(this.games.find(
+ g => g.type=="live" && g.players.some(pl => pl.sid == p.sid)));
+ break;
+ };
+ },
+ newChallenge: async function() {
+ const vname = this.getVname(this.newchallenge.vid);
+ const vModule = await import("@/variants/" + vname + ".js");
+ window.V = vModule.VariantRules;
+ if (!!this.newchallenge.timeControl.match(/^[0-9]+$/))
+ this.newchallenge.timeControl += "+0"; //assume minutes, no increment
+ const error = checkChallenge(this.newchallenge);
+ if (!!error)
+ return alert(error);
+ const ctype = this.classifyObject(this.newchallenge);
+ if (ctype == "corr" && this.st.user.id <= 0)
+ return alert("Please log in to play correspondance games");
+ // NOTE: "from" information is not required here
+ let chall = Object.assign({}, this.newchallenge);
+ const finishAddChallenge = (cid,warnDisconnected) => {
+ chall.id = cid || "c" + getRandString();
+ // Send challenge to peers (if connected)
+ this.sendSomethingTo(chall.to, "challenge", {chall:chall}, !!warnDisconnected);
+ chall.added = Date.now();
+ // NOTE: vname and type are redundant (can be deduced from timeControl + vid)
+ chall.type = ctype;
+ chall.vname = vname;
+ chall.from = { //decompose to avoid revealing email
+ sid: this.st.user.sid,
+ id: this.st.user.id,
+ name: this.st.user.name,
+ };
+ this.challenges.push(chall);
+ if (ctype == "live")
+ localStorage.setItem("challenge", JSON.stringify(chall));
+ document.getElementById("modalNewgame").checked = false;
+ };
+ const cIdx = this.challenges.findIndex(
+ c => c.from.sid == this.st.user.sid && c.type == ctype);
+ if (cIdx >= 0)
+ {
+ // Delete current challenge (will be replaced now)
+ this.sendSomethingTo(this.challenges[cIdx].to,
+ "deletechallenge", {cid:this.challenges[cIdx].id});
+ if (ctype == "corr")
+ {
+ ajax(
+ "/challenges",
+ "DELETE",
+ {id: this.challenges[cIdx].id}
+ );
+ }
+ this.challenges.splice(cIdx, 1);
+ }
+ if (ctype == "live")
+ {
+ // Live challenges have a random ID
+ finishAddChallenge(null, "warnDisconnected");
+ }
+ else
+ {
+ // Correspondance game: send challenge to server