],
data: function() {
return {
+ mobileBrowser: ("ontouchstart" in window),
possibleMoves: [], //filled after each valid click/dragstart
choices: [], //promotion pieces, or checkered captures... (as moves)
selectedPiece: null, //moving piece (or clicked piece)
}
let onEvents = {};
// NOTE: click = mousedown + mouseup
- if ("ontouchstart" in window) {
+ if (this.mobileBrowser) {
onEvents = {
on: {
touchstart: this.mousedown,
mousemove: function(e) {
if (!this.selectedPiece) return;
// There is an active element: move it around
- const [offsetX, offsetY] = e.clientX
- ? [e.clientX, e.clientY] //desktop browser
- : [e.changedTouches[0].pageX, e.changedTouches[0].pageY]; //smartphone
+ const [offsetX, offsetY] =
+ this.mobileBrowser
+ ? [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
+ : [e.clientX, e.clientY];
this.selectedPiece.style.left = offsetX - this.start.x + "px";
this.selectedPiece.style.top = offsetY - this.start.y + "px";
},
if (!this.selectedPiece) return;
// There is an active element: obtain the move from start and end squares
this.selectedPiece.style.zIndex = -3000; //HACK to find square from final coords
- const [offsetX, offsetY] = e.clientX
- ? [e.clientX, e.clientY]
- : [e.changedTouches[0].pageX, e.changedTouches[0].pageY];
+ const [offsetX, offsetY] =
+ this.mobileBrowser
+ ? [e.changedTouches[0].pageX, e.changedTouches[0].pageY]
+ : [e.clientX, e.clientY];
let landing = document.elementFromPoint(offsetX, offsetY);
this.selectedPiece.style.zIndex = 3000;
// Next condition: classList.contains(piece) fails because of marks
: "b";
if (g.score == "*") {
priority++;
- if (isMyTurn(g, myColor)) priority++;
+ if (g.turn == myColor || isMyTurn(g, myColor)) priority++;
}
}
if (g.created < minCreated) minCreated = g.created;
rematchOffer: "",
lastateAsked: false,
people: {}, //players + observers
- onMygames: [], //opponents (or me) on "MyGames" page
lastate: undefined, //used if opponent send lastate before game is ready
repeat: {}, //detect position repetition
curDiag: "", //for corr moves confirmation
this.drawOffer = "";
this.lastateAsked = false;
this.rematchOffer = "";
- this.onMygames = [];
this.lastate = undefined;
this.newChat = "";
this.roomInitialized = false;
params.socketUrl +
"/?sid=" +
this.st.user.sid +
+ "&id=" +
+ this.st.user.id +
"&tmpId=" +
getRandString() +
"&page=" +
getGameType: function(game) {
return game.cadence.indexOf("d") >= 0 ? "corr" : "live";
},
- // Notify turn after a new move (to opponent and me on MyGames page)
- notifyTurn: function(sid) {
- const player = this.people[sid];
- const colorIdx = this.game.players.findIndex(
- p => p.sid == sid || p.uid == player.id);
- const color = ["w","b"][colorIdx];
- const movesCount = this.game.moves.length;
- const yourTurn =
- (color == "w" && movesCount % 2 == 0) ||
- (color == "b" && movesCount % 2 == 1);
- this.send("turnchange", { target: sid, yourTurn: yourTurn });
+ // Notify something after a new move (to opponent and me on MyGames page)
+ notifyMyGames: function(thing, data) {
+ this.send(
+ "notify" + thing,
+ {
+ data: data,
+ targets: this.game.players.map(p => {
+ return { sid: p.sid, uid: p.uid };
+ })
+ }
+ );
},
showNextGame: function() {
// Did I play in current game? If not, add it to nextIds list
case "disconnect":
this.$delete(this.people, data.from);
break;
- case "mconnect": {
- // TODO: from MyGames page : send mconnect message with the list of gid (live and corr)
- // Either me (another tab) or opponent
- const sid = data.from;
- if (!this.onMygames.some(s => s == sid))
- {
- this.onMygames.push(sid);
- this.notifyTurn(sid); //TODO: this may require server ID (so, notify after receiving identity)
- }
- break;
- if (!this.people[sid])
- this.send("askidentity", { target: sid });
- }
- case "mdisconnect":
- ArrayFun.remove(this.onMygames, sid => sid == data.from);
- break;
case "getfocus": {
let player = this.people[data.from];
if (!!player) {
const notifyNewGame = () => {
let oppsid = this.getOppsid(); //may be null
this.send("rnewgame", { data: gameInfo, oppsid: oppsid });
+ // Also to MyGames page:
+ this.notifyMyGames("newgame", gameInfo);
};
if (this.game.type == "live")
this.addAndGotoLiveGame(gameInfo, notifyNewGame);
const score = this.vr.getCurrentScore();
if (score != "*") this.gameOver(score);
}
-// TODO: notifyTurn: "changeturn" message
this.game.moves.push(move);
this.game.fen = this.vr.getFen();
if (this.game.type == "live") {
// NOTE: 'var' to see that variable outside this block
var filtered_move = getFilteredMove(move);
}
+ if (moveCol == this.game.mycolor && !data.receiveMyMove) {
+ // Notify turn on MyGames page:
+ this.notifyMyGames(
+ "turn",
+ {
+ gid: this.gameRef.id,
+ turn: this.vr.turn
+ }
+ );
+ }
// Since corr games are stored at only one location, update should be
// done only by one player for each move:
if (
else this.updateCorrGame(scoreObj, callback);
// Notify the score to main Hall. TODO: only one player (currently double send)
this.send("result", { gid: this.game.id, score: score });
+ // Also to MyGames page (TODO: doubled as well...)
+ this.notifyMyGames(
+ "score",
+ {
+ gid: this.gameRef.id,
+ score: score
+ }
+ );
}
else if (!!callback) callback();
}
params.socketUrl +
"/?sid=" +
this.st.user.sid +
+ "&id=" +
+ this.st.user.id +
"&tmpId=" +
getRandString() +
"&page=" +
// Send game info (only if live) to everyone except me and opponent
// TODO: this double message send could be avoided.
this.send("newgame", { data: gameInfo, oppsid: oppsid });
+ // Also to MyGames page:
+ this.send(
+ "notifynewgame",
+ {
+ data: gameInfo,
+ targets: gameInfo.players.map(p => {
+ return { sid: p.sid, uid: p.uid };
+ })
+ }
+ );
};
if (c.type == "live") {
notifyNewgame();
params.socketUrl +
"/?sid=" +
this.st.user.sid +
+ "&id=" +
+ this.st.user.id +
"&tmpId=" +
getRandString() +
"&page=" +
elt.previousElementSibling.classList.remove("active");
else elt.nextElementSibling.classList.remove("active");
},
- // TODO: classifyObject is redundant (see Hall.vue)
- classifyObject: function(o) {
- return o.cadence.indexOf("d") === -1 ? "live" : "corr";
+ tryShowNewsIndicator: function(type) {
+ if (
+ (type == "live" && this.display == "corr") ||
+ (type == "corr" && this.display == "live")
+ ) {
+ document
+ .getElementById(type + "Games")
+ .classList.add("somethingnew");
+ }
+ },
+ socketMessageListener: function(msg) {
+ const data = JSON.parse(msg.data);
+ switch (data.code) {
+ // NOTE: no need to increment movesCount: unused if turn is provided
+ case "notifyturn":
+ case "notifyscore": {
+ const info = data.data;
+ let games =
+ !!parseInt(info.gid)
+ ? this.corrGames
+ : this.liveGames;
+ let g = games.find(g => g.id == info.gid);
+ // "notifything" --> "thing":
+ const thing = data.code.substr(6);
+ this.$set(g, thing, info[thing]);
+ this.tryShowNewsIndicator(g.type);
+ break;
+ }
+ case "notifynewgame": {
+ const gameInfo = data.data;
+ // st.variants might be uninitialized,
+ // if unlucky and newgame right after connect:
+ const v = this.st.variants.find(v => v.id == gameInfo.vid);
+ const vname = !!v ? v.name : "";
+ const type = gameInfo.cadence.indexOf('d') >= 0 ? "corr": "live";
+ const game = Object.assign(
+ {
+ vname: vname,
+ type: type,
+ score: "*"
+ },
+ gameInfo
+ );
+ this[type + "Games"].push(game);
+ this.tryShowNewsIndicator(type);
+ break;
+ }
+ }
+ },
+ socketCloseListener: function() {
+ this.conn = new WebSocket(this.connexionString);
+ this.conn.addEventListener("message", this.socketMessageListener);
+ this.conn.addEventListener("close", this.socketCloseListener);
},
showGame: function(game) {
// TODO: "isMyTurn" is duplicated (see GameList component). myColor also
}
);
}
- },
- socketMessageListener: function(msg) {
- const data = JSON.parse(msg.data);
- if (data.code == "changeturn") {
- let games = !!parseInt(data.gid)
- ? this.corrGames
- : this.liveGames;
- // NOTE: new move itself is not received, because it wouldn't be used.
- let g = games.find(g => g.id == data.gid);
- this.$set(g, "movesCount", g.movesCount + 1);
- if (
- (g.type == "live" && this.display == "corr") ||
- (g.type == "corr" && this.display == "live")
- ) {
- document
- .getElementById(g.type + "Games")
- .classList.add("somethingnew");
- }
- }
- },
- socketCloseListener: function() {
- this.conn = new WebSocket(this.connexionString);
- this.conn.addEventListener("message", this.socketMessageListener);
- this.conn.addEventListener("close", this.socketCloseListener);
}
}
};
// or "/mygames" for Mygames page (simpler: no 'people' array).
// tmpId is required if a same user (browser) has different tabs
let clients = {};
+ let sidToPages = {};
+ let idToSid = {};
wss.on("connection", (socket, req) => {
const query = getJsonFromUrl(req.url);
const sid = query["sid"];
+ const id = query["id"];
const tmpId = query["tmpId"];
const page = query["page"];
const notifyRoom = (page,code,obj={}) => {
delete clients[page][sid][tmpId];
if (Object.keys(clients[page][sid]).length == 0) {
delete clients[page][sid];
+ const pgIndex = sidToPages[sid].findIndex(pg => pg == page);
+ sidToPages[sid].splice(pgIndex, 1);
if (Object.keys(clients[page]).length == 0)
delete clients[page];
+ // Am I totally offline?
+ if (sidToPages[sid].length == 0) {
+ delete sidToPages[sid];
+ delete idToSid[id];
+ }
}
};
const doDisconnect = () => {
deleteConnexion();
- if (!clients[page] || !clients[page][sid]) {
+ // Nothing to notify when disconnecting from MyGames page:
+ if (page != "/mygames" && (!clients[page] || !clients[page][sid])) {
// I effectively disconnected from this page:
notifyRoom(page, "disconnect");
if (page.indexOf("/game/") >= 0)
});
// NOTE: a "gamer" could also just be an observer
Object.keys(clients).forEach(p => {
- if (p != "/") {
+ if (p.indexOf("/game/") >= 0) {
Object.keys(clients[p]).forEach(k => {
// 'page' indicator is needed for gamers
if (k != sid) sockIds.push({ sid:k, page:p });
notifyRoom("/", "result", { gid: obj.gid, score: obj.score });
break;
- case "mconnect":
- // Special case: notify some game rooms that
- // I'm watching game state from MyGames
- // TODO: this code is ignored for now
- obj.gids.forEach(gid => {
- const pg = "/game/" + gid;
- Object.keys(clients[pg]).forEach(s => {
- Object.keys(clients[pg][s]).forEach(x => {
- send(
- clients[pg][s][x].socket,
- { code: "mconnect", from: sid }
- );
- });
- });
- });
- break;
- case "mdisconnect":
- // TODO
- // Also TODO: pass newgame to MyGames, and gameover (result)
- break;
case "mabort": {
const gamePg = "/game/" + obj.gid;
if (!!clients[gamePg] && !!clients[gamePg][obj.target]) {
break;
}
+ case "notifyscore":
+ case "notifyturn":
+ case "notifynewgame":
+ if (!!clients["/mygames"]) {
+ obj.targets.forEach(t => {
+ const k = t.sid || idToSid[t.uid];
+ 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 (page == "/") notifyAllBut("/", obj.code, { 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);
});