Revert to news button in red, no blinking. Add autoplay option
[vchess.git] / client / src / views / Game.vue
index caf8dd8..ddbd330 100644 (file)
@@ -153,11 +153,8 @@ export default {
   data: function() {
     return {
       st: store.state,
-      gameRef: {
-        // rid = remote (socket) ID
-        id: "",
-        rid: ""
-      },
+      // gameRef can point to a corr game, local game or remote live game
+      gameRef: "",
       nextIds: [],
       game: {}, //passed to BaseGame
       // virtualClocks will be initialized from true game.clocks
@@ -203,13 +200,9 @@ export default {
           // In case of incomplete information variant:
           boardDiv.style.visibility = "hidden";
         this.atCreation();
-      } else {
+      } else
         // Same game ID
-        this.gameRef.id = to.params["id"];
-        this.gameRef.rid = to.query["rid"];
         this.nextIds = JSON.parse(this.$route.query["next"] || "[]");
-        this.fetchGame();
-      }
     }
   },
   // NOTE: some redundant code with Hall.vue (mostly related to people array)
@@ -244,11 +237,8 @@ export default {
     },
     atCreation: function() {
       // 0] (Re)Set variables
-      this.gameRef.id = this.$route.params["id"];
-      // rid = remote ID to find an observed live game,
-      // next = next corr games IDs to navigate faster
-      // (Both might be undefined)
-      this.gameRef.rid = this.$route.query["rid"];
+      this.gameRef = this.$route.params["id"];
+      // next = next corr games IDs to navigate faster (if applicable)
       this.nextIds = JSON.parse(this.$route.query["next"] || "[]");
       // Always add myself to players' list
       const my = this.st.user;
@@ -298,27 +288,27 @@ export default {
         // Discard potential "/?next=[...]" for page indication:
         encodeURIComponent(this.$route.path.match(/\/game\/[a-zA-Z0-9]+/)[0]);
       this.conn = new WebSocket(this.connexionString);
-      this.conn.onmessage = this.socketMessageListener;
-      this.conn.onclose = this.socketCloseListener;
+      this.conn.addEventListener("message", this.socketMessageListener);
+      this.conn.addEventListener("close", this.socketCloseListener);
       // Socket init required before loading remote game:
       const socketInit = callback => {
-        if (!!this.conn && this.conn.readyState == 1)
+        if (this.conn.readyState == 1)
           // 1 == OPEN state
           callback();
         else
           // Socket not ready yet (initial loading)
-          // NOTE: it's important to call callback without arguments,
-          // otherwise first arg is Websocket object and fetchGame fails.
+          // NOTE: first arg is Websocket object, unused here:
           this.conn.onopen = () => callback();
       };
-      if (!this.gameRef.rid)
-        // Game stored locally or on server
-        this.fetchGame(null, () => socketInit(this.roomInit));
-      else
-        // Game stored remotely: need socket to retrieve it
-        // NOTE: the callback "roomInit" will be lost, so we don't provide it.
-        // --> It will be given when receiving "fullgame" socket event.
-        socketInit(this.fetchGame);
+      this.fetchGame((game) => {
+        if (!!game)
+          this.loadVariantThenGame(game, () => socketInit(this.roomInit));
+        else
+          // Live game stored remotely: need socket to retrieve it
+          // NOTE: the callback "roomInit" will be lost, so we don't provide it.
+          // --> It will be given when receiving "fullgame" socket event.
+          socketInit(() => { this.send("askfullgame"); });
+      });
     },
     cleanBeforeDestroy: function() {
       if (!!this.askLastate)
@@ -432,13 +422,15 @@ export default {
       const currentUrl = document.location.href;
       const doAskGame = () => {
         if (document.location.href != currentUrl) return; //page change
-        if (!this.gameRef.rid)
-          // This is my game: just reload.
-          this.fetchGame();
-        else
-          // Just ask fullgame again (once!), this is much simpler.
-          // If this fails, the user could just reload page :/
-          this.send("askfullgame", { target: this.gameRef.rid });
+        this.fetchGame((game) => {
+          if (!!game)
+            // This is my game: just reload.
+            this.loadGame(game);
+          else
+            // Just ask fullgame again (once!), this is much simpler.
+            // If this fails, the user could just reload page :/
+            this.send("askfullgame");
+        });
       };
       // Delay of at least 2s between two game requests
       const now = Date.now();
@@ -451,6 +443,8 @@ export default {
       const data = JSON.parse(msg.data);
       switch (data.code) {
         case "pollclients":
+          // TODO: shuffling and random filtering on server, if
+          // the room is really crowded.
           data.sockIds.forEach(sid => {
             if (sid != this.st.user.sid) {
               this.people[sid] = { focus: true };
@@ -486,6 +480,8 @@ export default {
         }
         case "killed":
           // I logged in elsewhere:
+          this.conn.removeEventListener("message", this.socketMessageListener);
+          this.conn.removeEventListener("close", this.socketCloseListener);
           this.conn = null;
           alert(this.st.tr["New connexion detected: tab now offline"]);
           break;
@@ -563,8 +559,7 @@ export default {
               players: this.game.players,
               vid: this.game.vid,
               cadence: this.game.cadence,
-              score: this.game.score,
-              rid: this.st.user.sid //useful in Hall if I'm an observer
+              score: this.game.score
             };
             this.send("game", { data: myGame, target: data.from });
           }
@@ -574,7 +569,7 @@ export default {
             .filter(k =>
               [
                 "id","fen","players","vid","cadence","fenStart","vname",
-                "moves","clocks","initime","score","drawOffer","rematchOffer"
+                "moves","clocks","score","drawOffer","rematchOffer"
               ].includes(k))
             .reduce(
               (obj, k) => {
@@ -587,7 +582,7 @@ export default {
           break;
         case "fullgame":
           // Callback "roomInit" to poll clients only after game is loaded
-          this.fetchGame(data.data, this.roomInit);
+          this.loadVariantThenGame(data.data, this.roomInit);
           break;
         case "asklastate":
           // Sending informative last state if I played a move or score != "*"
@@ -598,13 +593,11 @@ export default {
         case "lastate": {
           // Got opponent infos about last move
           this.gotLastate = true;
-          if (!data.data.nothing) {
-            this.lastate = data.data;
-            if (this.game.rendered)
-              // Game is rendered (Board component)
-              this.processLastate();
-            // Else: will be processed when game is ready
-          }
+          this.lastate = data.data;
+          if (this.game.rendered)
+            // Game is rendered (Board component)
+            this.processLastate();
+          // Else: will be processed when game is ready
           break;
         }
         case "newmove": {
@@ -639,16 +632,15 @@ export default {
                   !!this.game.mycolor &&
                   !receiveMyMove
                 ) {
-                  GameStorage.update(this.gameRef.id, { drawOffer: "" });
+                  GameStorage.update(this.gameRef, { drawOffer: "" });
                 }
               }
               this.$refs["basegame"].play(movePlus.move, "received", null, true);
+              const moveColIdx = ["w", "b"].indexOf(movePlus.color);
+              this.game.clocks[moveColIdx] = movePlus.clock;
               this.processMove(
                 movePlus.move,
-                {
-                  clock: movePlus.clock,
-                  receiveMyMove: receiveMyMove
-                }
+                { receiveMyMove: receiveMyMove }
               );
             }
           }
@@ -656,15 +648,14 @@ export default {
         }
         case "gotmove": {
           this.opponentGotMove = true;
-          // Now his clock starts running:
+          // Now his clock starts running on my side:
           const oppIdx = ['w','b'].indexOf(this.vr.turn);
-          this.game.initime[oppIdx] = Date.now();
           this.re_setClocks();
           break;
         }
         case "resign":
-          const score = data.side == "b" ? "1-0" : "0-1";
-          const side = data.side == "w" ? "White" : "Black";
+          const score = (data.data == "b" ? "1-0" : "0-1");
+          const side = (data.data == "w" ? "White" : "Black");
           this.gameOver(score, side + " surrender");
           break;
         case "abort":
@@ -676,10 +667,22 @@ export default {
         case "drawoffer":
           // NOTE: observers don't know who offered draw
           this.drawOffer = "received";
+          if (this.game.type == "live") {
+            GameStorage.update(
+              this.gameRef,
+              { drawOffer: V.GetOppCol(this.game.mycolor) }
+            );
+          }
           break;
         case "rematchoffer":
           // NOTE: observers don't know who offered rematch
           this.rematchOffer = data.data ? "received" : "";
+          if (this.game.type == "live") {
+            GameStorage.update(
+              this.gameRef,
+              { rematchOffer: V.GetOppCol(this.game.mycolor) }
+            );
+          }
           break;
         case "newgame": {
           // A game started, redirect if I'm playing in
@@ -696,17 +699,7 @@ export default {
           ) {
             this.$router.push("/game/" + gameInfo.id);
           } else {
-            let urlRid = "";
-            if (gameInfo.cadence.indexOf('d') === -1) {
-              urlRid = "/?rid=";
-              // Select sid of any of the online players:
-              let onlineSid = [];
-              gameInfo.players.forEach(p => {
-                if (!!this.people[p.sid]) onlineSid.push(p.sid);
-              });
-              urlRid += onlineSid[Math.floor(Math.random() * onlineSid.length)];
-            }
-            this.rematchId = gameInfo.id + urlRid;
+            this.rematchId = gameInfo.id;
             document.getElementById("modalInfo").checked = true;
           }
           break;
@@ -729,7 +722,7 @@ export default {
         "PUT",
         {
           data: {
-            gid: this.gameRef.id,
+            gid: this.gameRef,
             newObj: obj
           },
           success: () => {
@@ -739,45 +732,43 @@ 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,
-          scoreMsg: 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 });
-      }
+      // 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.vr.turn != this.game.mycolor)
+            ? 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 != "*" ? this.game.score : undefined,
+        scoreMsg: this.game.score != "*" ? this.game.scoreMsg : undefined,
+        movesCount: L
+      };
+      this.send("lastate", { data: myLastate, target: target });
     },
     // lastate was received, but maybe game wasn't ready yet:
     processLastate: function() {
       const data = this.lastate;
       this.lastate = undefined; //security...
       const L = this.game.moves.length;
+      const oppIdx = 1 - ["w", "b"].indexOf(this.game.mycolor);
+      this.game.clocks[oppIdx] = data.clock;
       if (data.movesCount > L) {
         // Just got last move from him
         this.$refs["basegame"].play(data.lastMove, "received", null, true);
-        this.processMove(data.lastMove, { clock: data.clock });
+        this.processMove(data.lastMove);
+      } else {
+        clearInterval(this.clockUpdate);
+        this.re_setClocks();
       }
       if (data.drawSent) this.drawOffer = "received";
       if (data.rematchSent) this.rematchOffer = "received";
-      if (data.score != "*") {
+      if (!!data.score) {
         this.drawOffer = "";
         if (this.game.score == "*")
           this.gameOver(data.score, data.scoreMsg);
@@ -804,7 +795,7 @@ export default {
         this.send("drawoffer");
         if (this.game.type == "live") {
           GameStorage.update(
-            this.gameRef.id,
+            this.gameRef,
             { drawOffer: this.game.mycolor }
           );
         } else this.updateCorrGame({ drawOffer: this.game.mycolor });
@@ -822,7 +813,6 @@ export default {
           // Game state (including FEN): will be updated
           moves: [],
           clocks: [-1, -1], //-1 = unstarted
-          initime: [0, 0], //initialized later
           score: "*"
         }
       );
@@ -879,7 +869,7 @@ export default {
         this.send("rematchoffer", { data: true });
         if (this.game.type == "live") {
           GameStorage.update(
-            this.gameRef.id,
+            this.gameRef,
             { rematchOffer: this.game.mycolor }
           );
         } else this.updateCorrGame({ rematchOffer: this.game.mycolor });
@@ -889,7 +879,7 @@ export default {
         this.send("rematchoffer", { data: false });
         if (this.game.type == "live") {
           GameStorage.update(
-            this.gameRef.id,
+            this.gameRef,
             { rematchOffer: '' }
           );
         } else this.updateCorrGame({ rematchOffer: 'n' });
@@ -904,14 +894,10 @@ export default {
       if (!this.game.mycolor || !confirm(this.st.tr["Resign the game?"]))
         return;
       this.send("resign", { data: this.game.mycolor });
-      const score = this.game.mycolor == "w" ? "0-1" : "1-0";
-      const side = this.game.mycolor == "w" ? "White" : "Black";
+      const score = (this.game.mycolor == "w" ? "0-1" : "1-0");
+      const side = (this.game.mycolor == "w" ? "White" : "Black");
       this.gameOver(score, side + " surrender");
     },
-    // 3 cases for loading a game:
-    //  - from indexedDB (running or completed live game I play)
-    //  - 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) {
       this.vr = new V(game.fen);
       const gtype = this.getGameType(game);
@@ -922,16 +908,16 @@ export default {
       const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
       if (!game.chats) game.chats = []; //live games don't have chat history
       if (gtype == "corr") {
-        // NOTE: clocks in seconds, initime in milliseconds
+        // NOTE: clocks in seconds
         game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
         game.clocks = [tc.mainTime, tc.mainTime];
         const L = game.moves.length;
         if (game.score == "*") {
-          // Set clocks + initime
-          game.initime = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
-          if (L >= 1) game.initime[L % 2] = game.moves[L-1].played;
-          // NOTE: game.clocks shouldn't be computed right now:
-          // job will be done in re_setClocks() called soon below.
+          // Adjust clocks
+          if (L >= 2) {
+            game.clocks[L % 2] -=
+              (Date.now() - game.moves[L-1].played) / 1000;
+          }
         }
         // Sort chat messages from newest to oldest
         game.chats.sort((c1, c2) => {
@@ -957,16 +943,20 @@ export default {
         // Now that we used idx and played, re-format moves as for live games
         game.moves = game.moves.map(m => m.squares);
       }
-      if (gtype == "live" && game.clocks[0] < 0) {
-        // Game is unstarted. clocks and initime are ignored until move 2
-        game.clocks = [tc.mainTime, tc.mainTime];
-        game.initime = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
-        if (myIdx >= 0) {
-          // I play in this live game
-          GameStorage.update(game.id, {
-            clocks: game.clocks,
-            initime: game.initime
-          });
+      if (gtype == "live") {
+        if (game.clocks[0] < 0) {
+          // Game is unstarted. clock is ignored until move 2
+          game.clocks = [tc.mainTime, tc.mainTime];
+          if (myIdx >= 0) {
+            // I play in this live game
+            GameStorage.update(game.id, {
+              clocks: game.clocks
+            });
+          }
+        } else {
+          if (!!game.initime)
+            // It's my turn: clocks not updated yet
+            game.clocks[myIdx] -= (Date.now() - game.initime) / 1000;
         }
       }
       // TODO: merge next 2 "if" conditions
@@ -1052,82 +1042,66 @@ export default {
       }
       if (!!callback) callback();
     },
-    fetchGame: function(game, callback) {
-      const afterRetrieval = async (game) => {
-        await import("@/variants/" + game.vname + ".js")
-        .then((vModule) => {
-          window.V = vModule[game.vname + "Rules"];
-          this.loadGame(game, callback);
-        });
-      };
-      if (!!game) {
-        afterRetrieval(game);
-        return;
-      }
-      if (this.gameRef.rid)
-        // Remote live game: forgetting about callback func... (TODO: design)
-        this.send("askfullgame", { target: this.gameRef.rid });
-      else {
-        // Local or corr game on server.
-        // NOTE: afterRetrieval() is never called if game not found
-        const gid = this.gameRef.id;
-        if (Number.isInteger(gid) || !isNaN(parseInt(gid))) {
-          // corr games identifiers are integers
-          ajax(
-            "/games",
-            "GET",
-            {
-              data: { gid: gid },
-              success: (res) => {
-                res.game.moves.forEach(m => {
-                  m.squares = JSON.parse(m.squares);
-                });
-                afterRetrieval(res.game);
-              }
+    loadVariantThenGame: async function(game, callback) {
+      await import("@/variants/" + game.vname + ".js")
+      .then((vModule) => {
+        window.V = vModule[game.vname + "Rules"];
+        this.loadGame(game, callback);
+      });
+    },
+    // 3 cases for loading a game:
+    //  - from indexedDB (running or completed live game I play)
+    //  - from server (one correspondance game I play[ed] or not)
+    //  - from remote peer (one live game I don't play, finished or not)
+    fetchGame: function(callback) {
+      if (Number.isInteger(this.gameRef) || !isNaN(parseInt(this.gameRef))) {
+        // corr games identifiers are integers
+        ajax(
+          "/games",
+          "GET",
+          {
+            data: { gid: this.gameRef },
+            success: (res) => {
+              res.game.moves.forEach(m => {
+                m.squares = JSON.parse(m.squares);
+              });
+              callback(res.game);
             }
-          );
-        }
-        else
-          // Local game
-          GameStorage.get(this.gameRef.id, afterRetrieval);
-      }
+          }
+        );
+      } else
+        // Local game (or live remote)
+        GameStorage.get(this.gameRef, callback);
     },
     re_setClocks: function() {
+      this.virtualClocks = this.game.clocks.map(s => ppt(s).split(':'));
       if (this.game.moves.length < 2 || this.game.score != "*") {
         // 1st move not completed yet, or game over: freeze time
-        this.virtualClocks = this.game.clocks.map(s => ppt(s).split(':'));
         return;
       }
       const currentTurn = this.vr.turn;
       const currentMovesCount = this.game.moves.length;
       const colorIdx = ["w", "b"].indexOf(currentTurn);
-      let countdown =
-        this.game.clocks[colorIdx] -
-        (Date.now() - this.game.initime[colorIdx]) / 1000;
-      this.virtualClocks = [0, 1].map(i => {
-        const removeTime =
-          i == colorIdx ? (Date.now() - this.game.initime[colorIdx]) / 1000 : 0;
-        return ppt(this.game.clocks[i] - removeTime).split(':');
-      });
       this.clockUpdate = setInterval(
         () => {
           if (
-            countdown < 0 ||
+            this.game.clocks[colorIdx] < 0 ||
             this.game.moves.length > currentMovesCount ||
             this.game.score != "*"
           ) {
             clearInterval(this.clockUpdate);
-            if (countdown < 0)
+            if (this.game.clocks[colorIdx] < 0)
               this.gameOver(
                 currentTurn == "w" ? "0-1" : "1-0",
                 "Time"
               );
-          } else
+          } else {
             this.$set(
               this.virtualClocks,
               colorIdx,
-              ppt(Math.max(0, --countdown)).split(':')
+              ppt(Math.max(0, --this.game.clocks[colorIdx])).split(':')
             );
+          }
         },
         1000
       );
@@ -1136,47 +1110,43 @@ export default {
     processMove: function(move, data) {
       if (!data) data = {};
       const moveCol = this.vr.turn;
+      const colorIdx = ["w", "b"].indexOf(moveCol);
+      const nextIdx = 1 - colorIdx;
       const doProcessMove = () => {
-        const colorIdx = ["w", "b"].indexOf(moveCol);
-        const nextIdx = 1 - colorIdx;
         const origMovescount = this.game.moves.length;
-        let addTime = 0; //for live games
+        // The move is (about to be) played: stop clock
+        clearInterval(this.clockUpdate);
         if (moveCol == this.game.mycolor && !data.receiveMyMove) {
           if (this.drawOffer == "received")
             // I refuse draw
             this.drawOffer = "";
           if (this.game.type == "live" && origMovescount >= 2) {
-            const elapsed = Date.now() - this.game.initime[colorIdx];
-            // elapsed time is measured in milliseconds
-            addTime = this.game.increment - elapsed / 1000;
+            this.game.clocks[colorIdx] += this.game.increment;
+            // For a correct display in casqe of disconnected opponent:
+            this.$set(
+              this.virtualClocks,
+              colorIdx,
+              ppt(this.game.clocks[colorIdx]).split(':')
+            );
+            GameStorage.update(this.gameRef, {
+              // It's not my turn anymore:
+              initime: null
+            });
           }
         }
         // Update current game object:
         playMove(move, this.vr);
-        // The move is played: stop clock
-        clearInterval(this.clockUpdate);
-        if (!data.score) {
-          // Received move, score has not been computed in BaseGame (!!noemit)
-          const score = this.vr.getCurrentScore();
-          if (score != "*") this.gameOver(score);
-        }
+        if (!data.score)
+          // Received move, score is computed in BaseGame, but maybe not yet.
+          // ==> Compute it here, although this is redundant (TODO)
+          data.score = this.vr.getCurrentScore();
+        if (data.score != "*") this.gameOver(data.score);
         this.game.moves.push(move);
         this.game.fen = this.vr.getFen();
-        if (this.game.type == "live") {
-          if (!!data.clock) this.game.clocks[colorIdx] = data.clock;
-          else this.game.clocks[colorIdx] += addTime;
-        } else {
+        if (this.game.type == "corr") {
           // In corr games, just reset clock to mainTime:
           this.game.clocks[colorIdx] = extractTime(this.game.cadence).mainTime;
         }
-        // NOTE: opponent's initime is reset after "gotmove" is received
-        if (
-          !this.game.mycolor ||
-          moveCol != this.game.mycolor ||
-          !!data.receiveMyMove
-        ) {
-          this.game.initime[nextIdx] = Date.now();
-        }
         // If repetition detected, consider that a draw offer was received:
         const fenObj = this.vr.getFenForRepeat();
         this.repeat[fenObj] =
@@ -1194,13 +1164,26 @@ export default {
           this.notifyMyGames(
             "turn",
             {
-              gid: this.gameRef.id,
+              gid: this.gameRef,
               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 (
+          this.game.type == "live" &&
+          !!this.game.mycolor &&
+          moveCol != this.game.mycolor &&
+          this.game.moves.length >= 2
+        ) {
+          // Receive a move: update initime
+          this.game.initime = Date.now();
+          GameStorage.update(this.gameRef, {
+            // It's my turn now!
+            initime: this.game.initime
+          });
+        }
         if (
           !!this.game.mycolor &&
           !data.receiveMyMove &&
@@ -1232,12 +1215,11 @@ export default {
           }
           else {
             const updateStorage = () => {
-              GameStorage.update(this.gameRef.id, {
+              GameStorage.update(this.gameRef, {
                 fen: this.game.fen,
                 move: filtered_move,
                 moveIdx: origMovescount,
                 clocks: this.game.clocks,
-                initime: this.game.initime,
                 drawOffer: drawCode
               });
             };
@@ -1258,6 +1240,7 @@ export default {
           };
           if (this.game.type == "live")
             sendMove["clock"] = this.game.clocks[colorIdx];
+          // (Live) Clocks will re-start when the opponent pingback arrive
           this.opponentGotMove = false;
           this.send("newmove", {data: sendMove});
           // If the opponent doesn't reply gotmove soon enough, re-send move:
@@ -1305,6 +1288,7 @@ export default {
             // The board might have been hidden:
             if (boardDiv.style.visibility == "hidden")
               boardDiv.style.visibility = "visible";
+            if (data.score == "*") this.re_setClocks();
           }
         };
         let el = document.querySelector("#buttonsConfirm > .acceptBtn");
@@ -1370,7 +1354,7 @@ export default {
           scoreMsg: scoreMsg
         };
         if (this.game.type == "live") {
-          GameStorage.update(this.gameRef.id, scoreObj);
+          GameStorage.update(this.gameRef, scoreObj);
           if (!!callback) callback();
         }
         else this.updateCorrGame(scoreObj, callback);
@@ -1380,7 +1364,7 @@ export default {
         this.notifyMyGames(
           "score",
           {
-            gid: this.gameRef.id,
+            gid: this.gameRef,
             score: score
           }
         );
@@ -1425,7 +1409,7 @@ button
   margin: 0
   display: inline-flex
   img
-    height: 24px
+    height: 22px
     display: flex
     @media screen and (max-width: 767px)
       height: 18px