From: Benjamin Auder <benjamin.auder@somewhere> Date: Wed, 4 Dec 2019 10:26:36 +0000 (+0100) Subject: socket rooms correspnding to pages. TODO: Hall+Game (split live and corr? shared... X-Git-Url: https://git.auder.net/variants/Chakart/css/assets/current/pieces/%24%7BgetWhatsApp%28link%29%7D?a=commitdiff_plain;h=92a523d1a74cbabcfd7d6ade45f25fa622815f0b;p=vchess.git socket rooms correspnding to pages. TODO: Hall+Game (split live and corr? shared socket code?) --- diff --git a/client/src/App.vue b/client/src/App.vue index c5008f2e..a9c1d4cc 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -35,11 +35,10 @@ .row .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 footer - a(href="https://github.com/yagu0/vchess") {{ st.tr["Source code"] }} + router-link.menuitem(to="/about") {{ st.tr["About"] }} p.clickable(onClick="doClick('modalContact')") - | {{ st.tr["Contact form"] }} - //my-game(:game-ref="gameRef" :mode="mode" :settings="settings" @game-over="archiveGame") - //// TODO: add only the necessary icons to mini-css custom build + | {{ st.tr["Contact"] }} + // TODO: add only the necessary icons to mini-css custom build //script(src="//unpkg.com/feather-icons") </template> @@ -62,10 +61,6 @@ export default { st: store.state, }; }, -// // TODO: $route: ... -// gameRef: function() { -// this.loadGame(); -// }, computed: { flagImage: function() { return `/images/flags/${this.st.lang}.svg`; @@ -151,7 +146,7 @@ footer display: inline-flex align-items: center justify-content: center - & > a + & > .menuitem display: inline-block margin: 0 10px 0 0 &:link diff --git a/client/src/router.js b/client/src/router.js index 184fede0..380ccc6c 100644 --- a/client/src/router.js +++ b/client/src/router.js @@ -46,7 +46,7 @@ const router = new Router({ localStorage["myname"] = res.name; localStorage["myid"] = res.id; } - next(); + next("/"); } ); }, @@ -58,30 +58,26 @@ const router = new Router({ name: "game", component: loadView("Game"), }, -// { -// path: "/about", -// name: "about", -// // route level code-splitting -// // this generates a separate chunk (about.[hash].js) for this route -// // which is lazy-loaded when the route is visited. -// component: loadView('About'), -// //function() { -// // return import(/* webpackChunkName: "about" */ "./views/About.vue"); -// //} -// }, - // TODO: gameRef, problemId: https://router.vuejs.org/guide/essentials/dynamic-matching.html + { + path: "/about", + name: "about", + component: loadView("About"), + }, + // TODO: myGames, problemId: https://router.vuejs.org/guide/essentials/dynamic-matching.html ] }); router.beforeEach((to, from, next) => { - window.scrollTo(0, 0); //TODO: check if a live game is running; if yes, call next('/game') - //https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards + 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(); - //TODO: si une partie en cours dans storage, rediriger vers cette partie - //(à condition que l'URL n'y corresponde pas déjà !) - // (l'identifiant de l'utilisateur si connecté) -// if (!!localStorage["variant"]) -// location.hash = "#game?id=" + localStorage["gameId"]; + // TODO?: redirect to current game (through GameStorage.getCurrent()) if any? + // (and if the URL doesn't already match it) (use next("/game/GID")) + //https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards }); export default router; diff --git a/client/src/views/About.vue b/client/src/views/About.vue new file mode 100644 index 00000000..e4b76375 --- /dev/null +++ b/client/src/views/About.vue @@ -0,0 +1,36 @@ +<template lang="pug"> +.row + .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 + p TODO: give github URL, tell website story... + a(href="https://github.com/yagu0/vchess") contribute... +</template> + +<style lang="sass"> +.warn + padding: 3px + color: red + background-color: lightgrey + font-weight: bold + +p.boxed + background-color: #FFCC66 + padding: 5px + +.stageDelimiter + color: purple + +.section-title + padding: 0 + +.section-title > h4 + padding: 5px + +ol, ul:not(.browser-default) + padding-left: 20px + +ul:not(.browser-default) + margin-top: 5px + +ul:not(.browser-default) > li + list-style-type: disc +</style> diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 74dd01d2..d74002d6 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -27,15 +27,7 @@ // ==> après, implémenter/vérifier les passages de challenges + parties en cours // observer, // + problèmes, habiller et publier. (+ corr...) - // TODO: how to know who is observing ? Send message to everyone with game ID ? - // and then just listen to (dis)connect events - // server always send "connect on " + URL ; then add to observers if game... -// router when access a game page tell to server I joined + game ID (no need rid) -// and ask server for current joined (= observers) // when send to chat (or a move), reach only this group (send gid along) -// -> doivent être enregistrés comme observers au niveau du serveur... - // non: poll users + events startObserving / stopObserving - // (à faire au niveau du routeur ?) --> <script> @@ -60,13 +52,12 @@ export default { id: "", rid: "" }, - game: { }, //passed to BaseGame - oppConnected: false, //TODO: use for styling + game: {}, //passed to BaseGame corrMsg: "", //to send offline messages in corr games virtualClocks: [0, 0], //initialized with true game.clocks vr: null, //"variant rules" object initialized from FEN drawOffer: "", //TODO: use for button style - people: [ ], //potential observers (TODO) + people: [], //players + observers }; }, watch: { @@ -123,6 +114,11 @@ export default { this.game.vname = variantArray.filter(v => v.id == this.game.vid)[0].name; }, }, + computed: { + oppConnected: function() { + return this.people.indexOf(p => p.id == this.game.oppid) >= 0; + }, + }, created: function() { if (!!this.$route.params["id"]) { @@ -312,27 +308,34 @@ export default { if (!game.fen) game.fen = game.fenStart; //game wasn't started const gtype = (game.timeControl.indexOf('d') >= 0 ? "corr" : "live"); + const tc = extractTime(game.timeControl); if (gtype == "corr") { // corr game: needs to compute the clocks + initime - //if (game.players[i].rtime < 0) initime = Date.now(), else compute, - //also using move.played fields - game.clocks = [-1, -1]; + game.clocks = [tc.mainTime, tc.mainTime]; game.initime = [0, 0]; - // TODO: compute clocks + initime + let addTime = [0, 0]; + for (let i=2; i<game.moves.length; i++) + { + addTime[i%2] += tc.increment - + (game.moves[i].played - game.moves[i-1].played); + } + for (let i=0; i<=1; i++) + game.clocks[i] += addTime[i]; + const L = game.moves.length; + game.initime[L%2] = game.moves[L-1].played; } - const tc = extractTime(game.timeControl); // TODO: this is not really beautiful (uid on corr players...) if (gtype == "corr" && game.players[0].color == "b") [ game.players[0], game.players[1] ] = [ game.players[1], game.players[0] ]; const myIdx = game.players.findIndex(p => { return p.sid == this.st.user.sid || p.uid == this.st.user.id; }); - if (game.clocks[0] < 0) //game unstarted + if (gtype == "live" && game.clocks[0] < 0) //game unstarted { game.clocks = [tc.mainTime, tc.mainTime]; game.initime[0] = Date.now(); - if (myIdx >= 0 && gtype == "live") + if (myIdx >= 0) { // I play in this live game; corr games don't have clocks+initime GameStorage.update(game.id, diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 547395db..a8dfa987 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -90,7 +90,7 @@ export default { gdisplay: "live", games: [], challenges: [], - people: [], //(all) online players + people: [], //people in main hall infoMessage: "", newchallenge: { fen: "", @@ -581,7 +581,7 @@ export default { const game = Object.assign({}, gameInfo, { // (other) Game infos: constant fenStart: gameInfo.fen, - created: Date.now(), + added: Date.now(), // Game state (including FEN): will be updated moves: [], clocks: [-1, -1], //-1 = unstarted diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue index f626cc9e..f196e355 100644 --- a/client/src/views/Rules.vue +++ b/client/src/views/Rules.vue @@ -132,34 +132,34 @@ figure.diagram-container padding-top: 5px font-size: 0.8em - p.boxed - background-color: #FFCC66 - padding: 5px +p.boxed + background-color: #FFCC66 + padding: 5px - .stageDelimiter - color: purple +.stageDelimiter + color: purple - // To show (new) pieces, and/or there values... - figure.showPieces > img - width: 50px +// To show (new) pieces, and/or there values... +figure.showPieces > img + width: 50px - figure.showPieces > figcaption - color: #6C6C6C +figure.showPieces > figcaption + color: #6C6C6C - .section-title - padding: 0 +.section-title + padding: 0 - .section-title > h4 - padding: 5px +.section-title > h4 + padding: 5px - ol, ul:not(.browser-default) - padding-left: 20px +ol, ul:not(.browser-default) + padding-left: 20px - ul:not(.browser-default) - margin-top: 5px +ul:not(.browser-default) + margin-top: 5px - ul:not(.browser-default) > li - list-style-type: disc +ul:not(.browser-default) > li + list-style-type: disc .light-square-diag background-color: #e5e5ca diff --git a/server/db/create.sql b/server/db/create.sql index 0fb74564..3cfcbae3 100644 --- a/server/db/create.sql +++ b/server/db/create.sql @@ -43,7 +43,7 @@ create table Challenges ( foreign key (vid) references Variants(id) ); --- NOTE: no need for a "created" field, it's deduce from first move playing time +-- NOTE: no need for a "created" field, it's deduced from first move playing time create table Games ( id integer primary key, vid integer, @@ -59,7 +59,6 @@ create table Players ( gid integer, uid integer, color character, - rtime integer, --remaining time in milliseconds foreign key (gid) references Games(id), foreign key (uid) references Users(id) ); diff --git a/server/sockets.js b/server/sockets.js index 04422f46..fe82f6e3 100644 --- a/server/sockets.js +++ b/server/sockets.js @@ -20,12 +20,14 @@ module.exports = function(wss) { // TODO: later, allow duplicate connections (shouldn't be much more complicated) if (!!clients[sid]) return socket.send(JSON.stringify({code:"duplicate"})); - clients[sid] = socket; - // Notify room: - Object.keys(clients).forEach(k => { - if (k != sid) - clients[k].send(JSON.stringify({code:"connect",sid:sid})); - }); + clients[sid] = {sock: socket, page: query["page"]}; + const notifyRoom = (page,code) => { + Object.keys(clients).forEach(k => { + if (k != sid && clients[k].page == page) + clients[k].sock.send(JSON.stringify({code:code,sid:sid})); + }); + }; + notifyRoom(query["page"],"connect"); socket.on("message", objtxt => { let obj = JSON.parse(objtxt); if (!!obj.target && !clients[obj.target]) @@ -34,50 +36,56 @@ module.exports = function(wss) { { case "pollclients": socket.send(JSON.stringify({code:"pollclients", - sockIds:Object.keys(clients).filter(k => k != sid)})); + sockIds: Object.keys(clients).filter(k => + k != sid && clients[k].page == obj.page)})); + break; + case "pagechange": + notifyRoom(clients[sid].page, "disconnect"); + clients[sid].page = obj.page; + notifyRoom(obj.page, "connect"); break; case "askidentity": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"askidentity",from:sid})); break; case "askchallenge": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"askchallenge",from:sid})); break; case "askgame": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"askgame",from:sid})); break; case "identity": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"identity",user:obj.user})); break; case "refusechallenge": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"refusechallenge", cid:obj.cid, from:sid})); break; case "deletechallenge": - clients[obj.target].send( + clients[obj.target].sock.send( JSON.stringify({code:"deletechallenge", cid:obj.cid, from:sid})); break; case "newgame": - clients[obj.target].send(JSON.stringify( + clients[obj.target].sock.send(JSON.stringify( {code:"newgame", gameInfo:obj.gameInfo, cid:obj.cid})); break; case "challenge": - clients[obj.target].send(JSON.stringify( + clients[obj.target].sock.send(JSON.stringify( {code:"challenge", chall:obj.chall, from:sid})); break; case "game": - clients[obj.target].send(JSON.stringify( + clients[obj.target].sock.send(JSON.stringify( {code:"game", game:obj.game, from:sid})); break; case "newchat": - clients[obj.target].send(JSON.stringify({code:"newchat",msg:obj.msg})); + clients[obj.target].sock.send(JSON.stringify({code:"newchat",msg:obj.msg})); break; // TODO: WebRTC instead in this case (most demanding?) case "newmove": - clients[obj.target].send(JSON.stringify({code:"newmove",move:obj.move})); + clients[obj.target].sock.send(JSON.stringify({code:"newmove",move:obj.move})); break; case "ping": // If this code is reached, then obj.target is connected @@ -85,29 +93,27 @@ module.exports = function(wss) { break; case "lastate": const oppId = obj.target; - obj.oppid = sid; //I'm the opponent of my opponent(s) - clients[oppId].send(JSON.stringify(obj)); + obj.oppid = sid; //I'm the opponent of my opponent + clients[oppId].sock.send(JSON.stringify(obj)); break; case "resign": - clients[obj.target].send(JSON.stringify({code:"resign"})); + clients[obj.target].sock.send(JSON.stringify({code:"resign"})); break; case "abort": - clients[obj.target].send(JSON.stringify({code:"abort",msg:obj.msg})); + clients[obj.target].sock.send(JSON.stringify({code:"abort",msg:obj.msg})); break; case "drawoffer": - clients[obj.target].send(JSON.stringify({code:"drawoffer"})); + clients[obj.target].sock.send(JSON.stringify({code:"drawoffer"})); break; case "draw": - clients[obj.target].send(JSON.stringify({code:"draw"})); + clients[obj.target].sock.send(JSON.stringify({code:"draw"})); break; } }); socket.on("close", () => { + const page = clients[sid].page; delete clients[sid]; - // Notify every other connected client - Object.keys(clients).forEach( k => { - clients[k].send(JSON.stringify({code:"disconnect",sid:sid})); - }); + notifyRoom(page, "disconnect"); }); }); }