MPConnectivity: losing peer before receiving exit message

Hello. I am using MultipeerConnectivity framework in my SpriteKit game.


I have two scenarios when a peer leaves the game. First, when the peer leaves temporarily: connection is lost but all the nodes for this peer remain in the scene. And second: when the peer decides to leave for good. S/he selects an option "Exit the game" and sends the message to all other players saying that s/he is leaving. When the message is received by others, this peer's nodes should be removed from the scene.


This code is for exiting the game:


@objcfunc exitGame()
    {
        // Show an alert whether the player wants to exit the game
        let alert = UIAlertController(title: "", message: "Do you want to leave the game?", preferredStyle: UIAlertController.Style.alert)
     
        // If the player wants to leave the game
        let acceptAction: UIAlertAction = UIAlertAction(title: "Leave", style: UIAlertAction.Style.default)
        { _ in
         
            // Send a message to other players that you are exiting
            // Create data to send: the current deck is sent to the peers
            let messageToSend = self.makeMessageExit()  // A message is sent to inform others that I left
            self.appDelegate.connectionManager.sendData(dictionaryWithData: messageToSend,
                                                        toPeer: self.appDelegate.connectionManager.session.connectedPeers)
         
            DispatchQueue.main.async
                {
                    // Here goes the code to Remove the Menu with the Button Exit
                    if self.btnExitGame != nil && self.blurEffectView != nil && self.viewMenu != nil
                    {
                        self.btnExitGame.removeFromSuperview()
                        self.btnExitGame = nil
                     
                        self.blurEffectView.removeFromSuperview()
                        self.blurEffectView = nil
                     
                        self.viewMenu.removeFromSuperview()
                        self.viewMenu = nil
                    }
                 
                    // This closes the game scene
                    self.closeSceneToFindPlayers()
                 
                    // Deinitialize Connection Manager
                    self.appDelegate.connectionManager = nil
            }
        }
     
        // If the player declines to leave the game
        let declineAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel)
        { _ in
            
        }
     
        alert.addAction(acceptAction)
        alert.addAction(declineAction)
     
        OperationQueue.main.addOperation
        { () -> Void in
                self.view?.window?.rootViewController?.present(alert, animated: true, completion: nil)
        }
    }

This code is for losing the peer:


func lostPeer(peerID: MCPeerID)
    {
        print("I AM LOSING PEER.............")

        // If the peer temporarily left the game, change the status
        for (index, player) in self.appDelegate.players.enumerated()
        {
            if player.peerID?.displayName == peerID.displayName //&& player.status == "in" TODO: THIS IS REMOVED NEEDED??
            {
                self.appDelegate.players[index].status = "out_temp"
            }
        }
      
        // Display an alert message that the peer left
        let alert = UIAlertController(title: "Player Left",
                                      message: "\(peerID.displayName) left the game.",
                                      preferredStyle: UIAlertController.Style.alert)
      
        // Add an action (button)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
      
        OperationQueue.main.addOperation
        { () -> Void in
                     self.view?.window?.rootViewController?.present(alert, animated: true, completion: nil)
        }
      
        // Then I mark the leaving peer's nodes indicating that s/he left, but the nodes are still in the scene....
         drawFields()
    }


Here is the method to deal with the peer when s/he left for good when I receive the exit message from him/her:


func updateSceneExit(sender: MCPeerID?)
    {
        print("SCENE EXIT UPDATING............")

        // Remove peer from the list of players
        for player in appDelegate.players
        {
            if player.peerID?.displayName == sender?.displayName
            {
                appDelegate.players = appDelegate.players.filter(){$0 != player}
            }
        }
       
        // Redraw fields
        drawFields()

        print("SCENE EXIT UPDATED............",appDelegate.players)
    }


Now. In 80%-90% of cases, everything works fine. However when I have more than 2 players connected, sometimes what happens is that the connection with the peer is lost BEFORE I receive his/her Exit message. This results in the situation when the peer said that s/he had left, but I still have his/her nodes in the scene. Meanwhile, another player would still receive the message in advance resulting in no error.


Can someone please help me resolve the problem? Would be greatly appreciated!!!

Replies

OK. The best I could do it is to implement a protocol where a player wanting to exit sends a message to other peers; then the peers send the message back saying it is OK for the player to leave; once the OK message has been received from all connected peers, the Player disconnects.

Whereas the approach works with one or two peers connected, when I have three and more (up to 5), I constantly face the problem when the Player to whom other peers connected does not see that some other peers disconnected. I.e.:


Peer A sends a message to other peers that he wants to leave the game;

All peers send him a message back that it is OK for him to leave;

Peer A gets OK messages from everyone, exits the game and disconnects;

Peers B and C see that Peer A lost connection and update their scene. Peer D does not see that Peer A lost connection. It continues to treat as if Peer A is still in the game.


Method in my connection manager:


func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID)
    {
        for (index, aPeer) in foundPeers.enumerated()
        {
            if aPeer.displayName == peerID.displayName
            {
                foundPeers.remove(at: index)
                break
            }
        }
       
        delegate?.lostPeer(peerID: peerID)

        print("LOST PEERS:>>>>>>>>>>",foundPeers)
    }


Note that in my game, I do not allow connection of peers with the same name.


Could someone please help?


Thanks a lot!