From: Benjamin Auder Date: Sun, 18 Nov 2018 23:43:17 +0000 (+0100) Subject: First draft of game balancing solution after reconnect X-Git-Url: https://git.auder.net/doc/%7B%7B%20asset%28%27mixstore/current/img/config.php?a=commitdiff_plain;h=a29d9d6b7703d680ddb49cd3fe096f49b1d774f5;p=vchess.git First draft of game balancing solution after reconnect --- diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 160c692f..4a077417 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -1,4 +1,4 @@ -// TODO: use indexedDB instead of localStorage? (more flexible...) +// TODO: use indexedDB instead of localStorage? (more flexible: allow several games) Vue.component('my-game', { data: function() { @@ -365,14 +365,13 @@ Vue.component('my-game', { const socketOpenListener = () => { if (continuation) { - // TODO: check FEN integrity with opponent const fen = localStorage.getItem("fen"); const mycolor = localStorage.getItem("mycolor"); const oppid = localStorage.getItem("oppid"); const moves = JSON.parse(localStorage.getItem("moves")); this.newGame("human", fen, mycolor, oppid, moves, true); - // Send ping to server, which answers pong if opponent is connected - this.conn.send(JSON.stringify({code:"ping", oppid:this.oppId})); + // Send ping to server (answer pong if opponent is connected) + this.conn.send(JSON.stringify({code:"ping",oppid:this.oppId})); } else if (localStorage.getItem("newgame") === variant) { @@ -391,9 +390,46 @@ Vue.component('my-game', { case "newmove": //..he played! this.play(data.move, "animate"); break; - case "pong": //sent when opponent stayed online after we disconnected + case "pong": //received if opponent sent a ping this.oppConnected = true; + const L = this.vr.moves.length; + // Send our "last state" informations to opponent (we are still playing) + this.conn.send(JSON.stringify({ + code:"lastate", + oppid:this.oppId, + lastMove:L>0?this.vr.moves[L-1]:undefined, + movesCount:L, + })); break; + case "lastate": //got opponent infos about last move (we might have resigned) + if (this.mode!="human" || this.oppid!=data.oppid) + { + // OK, we resigned + this.conn.send(JSON.stringify({ + code:"lastate", + oppid:this.oppId, + lastMove:undefined, + movesCount:-1, + })); + } + else if (data.movesCount < 0) + { + // OK, he resigned + this.endGame(this.mycolor=="w"?"1-0":"0-1"); + } + else if (data.movesCount < this.vr.moves.length) + { + // We must tell last move to opponent + const L = this.vr.moves.length; + this.conn.send(JSON.stringify({ + code:"lastate", + oppid:this.oppId, + lastMove:this.vr.moves[L-1], + movesCount:L, + })); + } + else if (data.movesCount > this.vr.moves.length) //just got last move from him + this.play(data.lastMove, "animate"); case "resign": //..you won! this.endGame(this.mycolor=="w"?"1-0":"0-1"); break; @@ -433,7 +469,7 @@ Vue.component('my-game', { try { this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid})); } catch (INVALID_STATE_ERR) { - return; //resign failed for some reason... + return; //socket is not ready (and not yet reconnected) } } this.endGame(this.mycolor=="w"?"0-1":"1-0"); @@ -646,15 +682,7 @@ Vue.component('my-game', { this.incheck = this.vr.getCheckSquares(move, oppCol); //is opponent in check? // Not programmatic, or animation is over if (this.mode == "human" && this.vr.turn == this.mycolor) - { - if (!this.oppConnected) - return; //abort move if opponent is gone - try { - this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid})); - } catch(INVALID_STATE_ERR) { - return; //abort also if sending failed - } - } + this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid})); new Audio("/sounds/chessmove1.mp3").play(); this.vr.play(move, "ingame"); if (this.mode == "human") diff --git a/sockets.js b/sockets.js index f3d479d6..8548e2fe 100644 --- a/sockets.js +++ b/sockets.js @@ -1,16 +1,6 @@ -//const url = require('url'); +const url = require('url'); const Variants = require("./variants"); -function getJsonFromUrl(url) { - var query = url.substr(2); //starts with "/?" - var result = {}; - query.split("&").forEach(function(part) { - var item = part.split("="); - result[item[0]] = decodeURIComponent(item[1]); - }); - return result; -} - module.exports = function(wss) { let clients = { "index": {} }; @@ -18,21 +8,10 @@ module.exports = function(wss) { for (const v of Variants) clients[v.name] = {}; - // (resign, newgame, newmove). See https://github.com/websockets/ws/blob/master/lib/websocket.js around line 313 - // TODO: awaiting newmove, resign, (+newgame?) :: in memory structure (use Redis ?) - let newmoves = {}; - let newresign = {}; - for (const v of Variants) - { - newmoves[v.name] = {}; - newresign[v.name] = {}; - } - wss.on("connection", (socket, req) => { - //const params = new URL("http://localhost" + req.url).searchParams; - var query = getJsonFromUrl(req.url); - const sid = query["sid"]; //params.get("sid"); - const page = query["page"]; //params.get("page"); + const params = new URL("http://localhost" + req.url).searchParams; + const sid = params.get("sid"); + const page = params.get("page"); clients[page][sid] = socket; if (page == "index") { @@ -52,30 +31,22 @@ module.exports = function(wss) { Object.keys(clients[page]).forEach( k => { clients[page][k].send(JSON.stringify({code:"connect",id:sid})); }); - if (!!newmoves[page][sid]) - { - socket.send(JSON.stringify({code:"newmove",move:newmoves[page][sid]})); - delete newmoves[page][sid]; - } - if (!!newresign[page][sid]) - { - socket.send(JSON.stringify({code:"resign"})); - delete newresign[page][sid]; - } socket.on("message", objtxt => { let obj = JSON.parse(objtxt); switch (obj.code) { case "newmove": - if (!!clients[page][obj.oppid]) // && clients[page][obj.oppid].readyState == WebSocket.OPEN) + if (!!clients[page][obj.oppid]) clients[page][obj.oppid].send(JSON.stringify({code:"newmove",move:obj.move})); - else - newmoves[page][obj.oppid] = obj.move; break; case "ping": - if (!!clients[page][obj.oppid]) // && clients[page][obj.oppid].readyState == WebSocket.OPEN) + if (!!clients[page][obj.oppid]) socket.send(JSON.stringify({code:"pong"})); break; + case "lastate": + if (!!clients[page][obj.oppid]) + clients[page][obj.oppid].send(objtxt); + break; case "newgame": if (!!games[page]) { @@ -85,16 +56,15 @@ module.exports = function(wss) { delete games[page]; const mycolor = Math.random() < 0.5 ? 'w' : 'b'; socket.send(JSON.stringify({code:"newgame",fen:fen,oppid:oppId,color:mycolor})); - clients[page][oppId].send(JSON.stringify({code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w"})); + if (!!clients[page][oppId]) + clients[page][oppId].send(JSON.stringify({code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w"})); } else games[page] = {id:sid, fen:obj.fen}; //wait for opponent break; case "resign": - if (!!clients[page][obj.oppid]) // && clients[page][obj.oppid].readyState == WebSocket.OPEN) + if (!!clients[page][obj.oppid]) clients[page][obj.oppid].send(JSON.stringify({code:"resign"})); - else - newresign[page][obj.oppid] = true; break; } });