javascriptsocket.io

Handle disconnects in multiplayer JavaScript game


I am creating an online multiplayer game in JavaScript (JS), think Lichess or Chess.com but for another game. I now want to detect if a user disconnects but having trouble figuring out how to do this reliably. It doesn't matter if user closes the tab, the window, or loses internet connection. I need to know if any of the events happen. However, they should be allowed to switch tabs, windows or similar, as long as they don't close the game.

I use socket.io for websocket communication.

Problems and what I have tried

So far I have tried some different solutions:

  1. Handle it in sockets disconnect event: socket.on('disconnect', () => { console.log('User disconnected) }); But this is not reliable and doesn't always fire.

  2. Using a ping/pong system where client sends a signal to server to let it know it is alive. But if I switch tab or window it seems that JS starts throttling, or even stops completely.

    socket.on("connect", () => {     
        if (!heartbeatInterval) {
            heartbeatInterval = setInterval(() => {
                socket.emit("pong");
            }, 4000);
        }
    });
    
  3. To get around the problem in point 2 I tried using a Web Worker. The problem with that is that even if I closes a tab or window it keeps going. I tried stopping it in window.addEventListener("beforeunload", () => {... but it doesn't always fire, so basically opposit problem to point 2.

     heartbeatWorker = new Worker('/heartbeatWorker.js');
     heartbeatWorker.onmessage = () => {
         socket.emit("pong");
     };
     heartbeatWorker.postMessage('start');
    

Question

How can I create a reliable disconnect system similar to Lichess or Chess.com with above requirements?


Solution

  • It is true that the socket disconnect event does not always fire upon closing the tab. However, it seems that you can send off one message in the onbeforeunload handler. You don't need a WebWorker.

    window.addEventListener("beforeunload", function(event) {
        socket.emit("closePage");
    });
    

    Then you can have a listener on the server for both the socket disconnect event and for the closePage event. From my experience, this closePage will always fire when the user closes the tab or window, or restarts their PC. The disconnect will usually fire right when they lose internet connection, or will fire after a timeout period. As long as the tab is open and the computer is on neither of these fires.

    The situation on mobile is more complicated since mobile OSs like to suspend pages and connections when they aren't active. However, the closePage event still fires when the tab is closed. The browser usually completes a disconnect when it suspends a page, so the disconnect event will fire on the server