Fix rematch process
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 9 Mar 2020 14:28:19 +0000 (15:28 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 9 Mar 2020 14:28:19 +0000 (15:28 +0100)
client/src/views/Game.vue
client/src/views/Hall.vue
server/models/Game.js
server/routes/games.js
server/sockets.js

index f24430b..024847c 100644 (file)
@@ -163,6 +163,7 @@ export default {
       drawOffer: "",
       infoMessage: "",
       rematchOffer: "",
+      lastateAsked: false,
       people: {}, //players + observers
       onMygames: [], //opponents (or me) on "MyGames" page
       lastate: undefined, //used if opponent send lastate before game is ready
@@ -268,6 +269,7 @@ export default {
       this.virtualClocks = [[0,0], [0,0]];
       this.vr = null;
       this.drawOffer = "";
+      this.lastateAsked = false;
       this.rematchOffer = "";
       this.onMygames = [];
       this.lastate = undefined;
@@ -396,6 +398,9 @@ export default {
         this.$set(this.game, "chats", []);
       }
     },
+    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];
@@ -596,31 +601,9 @@ export default {
           break;
         case "asklastate":
           // Sending informative last state if I played a move or score != "*"
-          if (
-            (this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) ||
-            this.game.score != "*" ||
-            this.drawOffer == "sent" ||
-            this.rematchOffer == "sent"
-          ) {
-            // Send our "last state" informations to opponent
-            const L = this.game.moves.length;
-            const myIdx = ["w", "b"].indexOf(this.game.mycolor);
-            const myLastate = {
-              lastMove: L > 0 ? this.game.moves[L - 1] : undefined,
-              clock: this.game.clocks[myIdx],
-              // Since we played a move (or abort or resign),
-              // only drawOffer=="sent" is possible
-              drawSent: this.drawOffer == "sent",
-              rematchSent: this.rematchOffer == "sent",
-              score: this.game.score,
-              score: this.game.scoreMsg,
-              movesCount: L,
-              initime: this.game.initime[1 - myIdx] //relevant only if I played
-            };
-            this.send("lastate", { data: myLastate, target: data.from });
-          } else {
-            this.send("lastate", { data: {nothing: true}, target: data.from });
-          }
+          // If the game or moves aren't loaded yet, delay the sending:
+          if (!this.game || !this.game.moves) this.lastateAsked = true;
+          else this.sendLastate(data.from);
           break;
         case "lastate": {
           // Got opponent infos about last move
@@ -711,9 +694,15 @@ export default {
         case "newgame": {
           // A game started, redirect if I'm playing in
           const gameInfo = data.data;
+          const gameType = this.getGameType(gameInfo);
           if (
-            gameInfo.players.some(p =>
-              p.sid == this.st.user.sid || p.uid == this.st.user.id)
+            gameType == "live" &&
+            gameInfo.players.some(p => p.sid == this.st.user.sid)
+          ) {
+            this.addAndGotoLiveGame(gameInfo);
+          } else if (
+            gameType == "corr" &&
+            gameInfo.players.some(p => p.uid == this.st.user.id)
           ) {
             this.$router.push("/game/" + gameInfo.id);
           } else {
@@ -766,6 +755,33 @@ export default {
         }
       );
     },
+    sendLastate: function(target) {
+      if (
+        (this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) ||
+        this.game.score != "*" ||
+        this.drawOffer == "sent" ||
+        this.rematchOffer == "sent"
+      ) {
+        // Send our "last state" informations to opponent
+        const L = this.game.moves.length;
+        const myIdx = ["w", "b"].indexOf(this.game.mycolor);
+        const myLastate = {
+          lastMove: L > 0 ? this.game.moves[L - 1] : undefined,
+          clock: this.game.clocks[myIdx],
+          // Since we played a move (or abort or resign),
+          // only drawOffer=="sent" is possible
+          drawSent: this.drawOffer == "sent",
+          rematchSent: this.rematchOffer == "sent",
+          score: this.game.score,
+          score: this.game.scoreMsg,
+          movesCount: L,
+          initime: this.game.initime[1 - myIdx] //relevant only if I played
+        };
+        this.send("lastate", { data: myLastate, target: target });
+      } else {
+        this.send("lastate", { data: {nothing: true}, target: target });
+      }
+    },
     // lastate was received, but maybe game wasn't ready yet:
     processLastate: function() {
       const data = this.lastate;
@@ -811,6 +827,32 @@ export default {
         } else this.updateCorrGame({ drawOffer: this.game.mycolor });
       }
     },
+    addAndGotoLiveGame: function(gameInfo, callback) {
+      const game = Object.assign(
+        {},
+        gameInfo,
+        {
+          // (other) Game infos: constant
+          fenStart: gameInfo.fen,
+          vname: this.game.vname,
+          created: Date.now(),
+          // Game state (including FEN): will be updated
+          moves: [],
+          clocks: [-1, -1], //-1 = unstarted
+          initime: [0, 0], //initialized later
+          score: "*"
+        }
+      );
+      GameStorage.add(game, (err) => {
+        // No error expected.
+        if (!err) {
+          if (this.st.settings.sound)
+            new Audio("/sounds/newgame.flac").play().catch(() => {});
+          callback();
+          this.$router.push("/game/" + gameInfo.id);
+        }
+      });
+    },
     clickRematch: function() {
       if (!this.game.mycolor) return; //I'm just spectator
       if (this.rematchOffer == "received") {
@@ -822,33 +864,12 @@ export default {
           vid: this.game.vid,
           cadence: this.game.cadence
         };
-        let oppsid = this.getOppsid(); //may be null
-        this.send("rnewgame", { data: gameInfo, oppsid: oppsid });
-        if (this.game.type == "live") {
-          const game = Object.assign(
-            {},
-            gameInfo,
-            {
-              // (other) Game infos: constant
-              fenStart: gameInfo.fen,
-              vname: this.game.vname,
-              created: Date.now(),
-              // Game state (including FEN): will be updated
-              moves: [],
-              clocks: [-1, -1], //-1 = unstarted
-              initime: [0, 0], //initialized later
-              score: "*"
-            }
-          );
-          GameStorage.add(game, (err) => {
-            // No error expected.
-            if (!err) {
-              if (this.st.settings.sound)
-                new Audio("/sounds/newgame.flac").play().catch(() => {});
-              this.$router.push("/game/" + gameInfo.id);
-            }
-          });
-        }
+        const notifyNewGame = () => {
+          let oppsid = this.getOppsid(); //may be null
+          this.send("rnewgame", { data: gameInfo, oppsid: oppsid });
+        };
+        if (this.game.type == "live")
+          this.addAndGotoLiveGame(gameInfo, notifyNewGame);
         else {
           // corr game
           ajax(
@@ -859,6 +880,7 @@ export default {
               data: { gameInfo: gameInfo },
               success: (response) => {
                 gameInfo.id = response.gameId;
+                notifyNewGame();
                 this.$router.push("/game/" + response.gameId);
               }
             }
@@ -903,11 +925,11 @@ export default {
     //  - from server (one correspondance game I play[ed] or not)
     //  - from remote peer (one live game I don't play, finished or not)
     loadGame: function(game, callback) {
-      const afterRetrieval = async game => {
+      const afterRetrieval = async (game) => {
         const vModule = await import("@/variants/" + game.vname + ".js");
         window.V = vModule.VariantRules;
         this.vr = new V(game.fen);
-        const gtype = game.cadence.indexOf("d") >= 0 ? "corr" : "live";
+        const gtype = this.getGameType(game);
         const tc = extractTime(game.cadence);
         const myIdx = game.players.findIndex(p => {
           return p.sid == this.st.user.sid || p.uid == this.st.user.id;
@@ -1050,6 +1072,10 @@ export default {
           // Did lastate arrive before game was rendered?
           if (this.lastate) this.processLastate();
         });
+        if (this.lastateAsked) {
+          this.lastateAsked = false;
+          this.sendLastate(game.oppsid);
+        }
         if (this.gameIsLoading) {
           this.gameIsLoading = false;
           if (this.gotMoveIdx >= game.moves.length)
index 4cb46ec..ac2e26e 100644 (file)
@@ -685,7 +685,7 @@ export default {
           const game = data.data;
           // Ignore games where I play (will go in MyGames page)
           if (game.players.every(p =>
-            p.sid != this.st.user.sid || p.id != this.st.user.id))
+            p.sid != this.st.user.sid || p.uid != this.st.user.id))
           {
             let locGame = this.games.find(g => g.id == game.id);
             if (!locGame) {
@@ -948,6 +948,16 @@ export default {
     },
     // NOTE: when launching game, the challenge is already being deleted
     launchGame: function(c) {
+      let players =
+        !!c.mycolor
+          ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat])
+          : shuffle([c.from, c.seat]);
+      // Convention for players IDs in stored games is 'uid'
+      players.forEach(p => {
+        let pWithUid = p;
+        pWithUid["uid"] = p.id;
+        delete pWithUid["id"];
+      });
       // These game informations will be shared
       let gameInfo = {
         id: getRandString(),
index e45c182..caa15c3 100644 (file)
@@ -39,7 +39,7 @@ const GameModel =
       g.cadence.match(/^[0-9dhms +]+$/) &&
       g.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
       g.players.length == 2 &&
-      g.players.every(p => p.id.toString().match(/^[0-9]+$/))
+      g.players.every(p => p.uid.toString().match(/^[0-9]+$/))
     );
   },
 
@@ -60,7 +60,7 @@ const GameModel =
             const color = (idx==0 ? "w" : "b");
             query =
               "INSERT INTO Players VALUES " +
-              "(" + this.lastID + "," + p.id + ",'" + color + "')";
+              "(" + this.lastID + "," + p.uid + ",'" + color + "')";
             db.run(query);
           });
           cb(null, {gid: this.lastID});
@@ -99,7 +99,8 @@ const GameModel =
               "FROM Chats " +
               "WHERE gid = " + id;
             db.all(query, (err4, chats) => {
-              const game = Object.assign({},
+              const game = Object.assign(
+                {},
                 gameInfo,
                 {
                   players: players,
@@ -140,7 +141,8 @@ const GameModel =
             "FROM Moves " +
             "WHERE gid = " + id;
           db.get(query, (err,ret) => {
-            const game = Object.assign({},
+            const game = Object.assign(
+              {},
               gameInfo,
               {
                 players: players,
index 21aee47..a86a76d 100644 (file)
@@ -12,7 +12,7 @@ router.post("/games", access.logged, access.ajax, (req,res) => {
   const cid = req.body.cid;
   if (
     Array.isArray(gameInfo.players) &&
-    gameInfo.players.some(p => p.id == req.userId) &&
+    gameInfo.players.some(p => p.uid == req.userId) &&
     (!cid || cid.toString().match(/^[0-9]+$/)) &&
     GameModel.checkGameInfo(gameInfo)
   ) {
index 7395af1..d1489f7 100644 (file)
@@ -205,7 +205,7 @@ module.exports = function(wss) {
 
         case "rnewgame":
           // A rematch game started: players are already informed
-          notifyAllBut(page, "newgame", {data: obj.data}, [sid, obj.oppsid]);
+          notifyAllBut(page, "newgame", {data: obj.data}, [sid]);
           notifyAllBut("/", "newgame", {data: obj.data}, [sid, obj.oppsid]);
           notifyRoom("/mygames", "newgame", {data: obj.data});
           break;