Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Join us on Thursday, June 8, for a Q&A with Unity's Content Pipeline group here on the forum, and on the Unity Discord, and discuss topics around Content Build, Import Workflows, Asset Database, and Addressables!
    Dismiss Notice

Unity Multiplayer Preserving objects with Local Player Authority after client disconnection

Discussion in 'Multiplayer' started by Lork, Oct 19, 2015.

  1. Lork

    Lork

    Joined:
    Jul 12, 2013
    Posts:
    79
    Currently when a client disconnects, any objects with local player authority owned by that player are destroyed. I don't want this behavior. Instead, they should continue to exist and their ownership should be passed back to the server.

    Is there any way to achieve this? Either changing the functionality globally or a way to "save" them individually would work, as long as it doesn't involve remaking them from scratch along with everything that would entail.
     
    Kokowolo likes this.
  2. irwiss

    irwiss

    Joined:
    Apr 14, 2015
    Posts:
    7
    Would also like to know if there's a solution for this.

    I've a half-working solution where I remove client authority from non-player objects, that stops the non-player objects from disappearing (otherwise they go poof as well).

    With player objects that does not work since they're handled differently. I tried to use ReplacePlayerForConnection to replace player objects with empty dummy objects before calling NetworkManagers' OnServerDisconnect.

    That doesn't work - invoking base.OnServerDisconnect destroys all (former) player objects regardless of ReplacePlayerForConnection returning true for success.

    Not invoking it keeps the player object intact but produces a warning "Player not destroyed when connection disconnected." and I'm guessing corrupts internal lists with obsolete items.

    At that point I stopped since this was taking way too much time to delve into decompiled code. Once I get back to this I suppose I'll use a dummy "player object" that's sitting pretty and an object that gets assigned authority at start and serves as an actual player object as a workaround for this issue.

    It doesn't seem right that an object goes poof on disconnection without any means to stop it, so may be I missed some more elegant method?

    Code (csharp):
    1.  
    2.         public override void OnServerDisconnect(NetworkConnection conn)
    3.         {
    4.             NetworkInstanceId[] clientObjects = new NetworkInstanceId[conn.clientOwnedObjects.Count];
    5.             conn.clientOwnedObjects.CopyTo(clientObjects);
    6.  
    7.             foreach (NetworkInstanceId objId in clientObjects)
    8.             {
    9.                 NetworkIdentity netIdentity = NetworkServer.FindLocalObject(objId).GetComponent<NetworkIdentity>();
    10.  
    11.                 if (netIdentity.connectionToClient == null)
    12.                 {
    13.                     netIdentity.RemoveClientAuthority(conn);
    14.                 }
    15.                 else
    16.                 {
    17.                     // This branch does not work properly
    18.                     NetworkIdentity dummyObject = Instantiate(EmptySpawnablePrefab);
    19.  
    20.                     if (!NetworkServer.ReplacePlayerForConnection(conn, dummyObject.gameObject, netIdentity.playerControllerId))
    21.                         Debug.LogErrorFormat("Couldn't replace player for connection: {0} controllerId: {1}", conn.connectionId, netIdentity.playerControllerId);
    22.                 }
    23.             }
    24.  
    25.             // following line destroys (former) player objects, even if they've been successfully replaced by NetworkServer.ReplacePlayerForConnection
    26.             base.OnServerDisconnect(conn);
    27.         }
    28.  
    29.  
     
    Kokowolo likes this.
  3. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    calling RemoveClientAuthority for the non-player owned objects in OnServerDisconnect should be fine.

    If you are handling player object management, try not calling base.OnServerDisconnect().
     
  4. wadybaby7861

    wadybaby7861

    Joined:
    Apr 17, 2020
    Posts:
    4
    I know this is very old, but I let my clients de-Parent the objects.

    Use gameObject.transform.SetParent(null); below

    Code (CSharp):
    1.     public override void OnServerConnect(NetworkConnection conn)
    2.     {
    3.  
    4.         Debug.Log($" Player {conn.connectionId} has connected.");
    5.         if (conn.connectionId != 0)
    6.         {
    7.             currentPlayers += 1;
    8.         }
    9.         Debug.Log($"Total Players: {currentPlayers}");
    10.     }
    11.  
    12.     public override void OnServerDisconnect(NetworkConnection conn)
    13.     {
    14.         Debug.Log($" Player {conn.connectionId} has disconnected.");
    15.         if (conn.connectionId != 0)
    16.         {
    17.             currentPlayers -= 1;
    18.         }
    19.         Debug.Log($"Total Players: {currentPlayers}");
    20.  
    21.         // Remove authority to players that disconnect the match so they don't disappear
    22.         List<NetworkInstanceId> networkIds = new List<NetworkInstanceId>(conn.clientOwnedObjects);
    23.  
    24.         foreach (NetworkInstanceId netId in networkIds)
    25.         {
    26.  
    27.             GameObject gameObject = NetworkServer.FindLocalObject(netId);
    28.             NetworkIdentity netIdentity = gameObject.GetComponent<NetworkIdentity>();
    29.  
    30.             bool isPlayer = netIdentity.GetComponent<MoveCube>() != null;
    31.             if (!isPlayer)
    32.             {
    33.                 gameObject.transform.SetParent(null);
    34.                 netIdentity.RemoveClientAuthority(conn);
    35.             }
    36.         }
    37.  
    38.         NetworkServer.DestroyPlayersForConnection(conn);
    39.         if (conn.lastError != NetworkError.Ok)
    40.         {
    41.             if (LogFilter.logError)
    42.             {
    43.                 Debug.LogError("ServerDisconnected due to error: " + conn.lastError);
    44.             }
    45.         }
    46.     }