Preserving objects with Local Player Authority after client disconnection

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.

1 Like

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?

        public override void OnServerDisconnect(NetworkConnection conn)
        {
            NetworkInstanceId[] clientObjects = new NetworkInstanceId[conn.clientOwnedObjects.Count];
            conn.clientOwnedObjects.CopyTo(clientObjects);

            foreach (NetworkInstanceId objId in clientObjects)
            {
                NetworkIdentity netIdentity = NetworkServer.FindLocalObject(objId).GetComponent<NetworkIdentity>();

                if (netIdentity.connectionToClient == null)
                {
                    netIdentity.RemoveClientAuthority(conn);
                }
                else
                {
                    // This branch does not work properly
                    NetworkIdentity dummyObject = Instantiate(EmptySpawnablePrefab);

                    if (!NetworkServer.ReplacePlayerForConnection(conn, dummyObject.gameObject, netIdentity.playerControllerId))
                        Debug.LogErrorFormat("Couldn't replace player for connection: {0} controllerId: {1}", conn.connectionId, netIdentity.playerControllerId);
                }
            }

            // following line destroys (former) player objects, even if they've been successfully replaced by NetworkServer.ReplacePlayerForConnection
            base.OnServerDisconnect(conn);
        }
1 Like

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().

I know this is very old, but I let my clients de-Parent the objects.

Use gameObject.transform.SetParent(null); below

    public override void OnServerConnect(NetworkConnection conn)
    {

        Debug.Log($" Player {conn.connectionId} has connected.");
        if (conn.connectionId != 0)
        {
            currentPlayers += 1;
        }
        Debug.Log($"Total Players: {currentPlayers}");
    }

    public override void OnServerDisconnect(NetworkConnection conn)
    {
        Debug.Log($" Player {conn.connectionId} has disconnected.");
        if (conn.connectionId != 0)
        {
            currentPlayers -= 1;
        }
        Debug.Log($"Total Players: {currentPlayers}");

        // Remove authority to players that disconnect the match so they don't disappear
        List<NetworkInstanceId> networkIds = new List<NetworkInstanceId>(conn.clientOwnedObjects);

        foreach (NetworkInstanceId netId in networkIds)
        {

            GameObject gameObject = NetworkServer.FindLocalObject(netId);
            NetworkIdentity netIdentity = gameObject.GetComponent<NetworkIdentity>();

            bool isPlayer = netIdentity.GetComponent<MoveCube>() != null;
            if (!isPlayer)
            {
                gameObject.transform.SetParent(null);
                netIdentity.RemoveClientAuthority(conn);
            }
        }

        NetworkServer.DestroyPlayersForConnection(conn);
        if (conn.lastError != NetworkError.Ok)
        {
            if (LogFilter.logError)
            {
                Debug.LogError("ServerDisconnected due to error: " + conn.lastError);
            }
        }
    }