#What happens with unspawn player objects when the owner disconnects?

11 messages · Page 1 of 1 (latest)

vestal wraith
#

I have some ghost player objects in my server, basically objects owned by players that have disconnected.
I have no ideia why these objects persist after a disconnect, my guess is that I don't understand how the NetworkServer.RemovePlayerForConnection(conn, RemovePlayerOptions.Unspawn); works, and maybe this is why I have these ghosts?

regal olive
vestal wraith
regal olive
#

@subtle spindle or @sullen shore Will have to work on this one ^

regal olive
# vestal wraith Awesome! I can hunt those ghosts now.

I don't know if this was saracasm, but if you have a custom network manager overriding OnServerDisconnect and you don't call the base method from there, you're preventing Mirror from cleaning up after the client that disconnected. By default, Mirror destroys on server and all clients anything the client owned, including its Player object.

sullen shore
regal olive
vestal wraith
#

I might have fixed this bug on my side, here are my findings:
This is related to the "ghosts" problem I was having. Basically, my game relies on a room-based system, when a player changes room, the server will perform the following sequence:

  1. Send a scene unload msg to client.
  2. Wait for fade in to happen.
  3. Unspawn the connection identity and destroy other owned objects.
  4. Wait one frame for interest management to happen. <-- bug happens if player disconnects here
  5. Add player for connection and send other messages.

In that single frame, if the player disconnects, the identity won't be destroyed because we unspawned it. We would need to destroy it if NetworkServer.AddPlayerForConnection fail, which never happens!
As a result, the player identity then stays alive, holding the NetworkConnectionToClient, which keeps enqueuing messages in the batcher that are never sent over.

I fixed this by adding the following check inside of NetworkServer.AddPlayerForConnection:

 {
     Debug.LogError($"AddPlayer: connection {conn} not found in server connections");
        return false;
 }```

I also got this console error:
```Still had 1 batches remaining after processing, even though processing was not interrupted by a scene change. This should never happen, as it would cause ever growing batches.
Possible reasons:
* A message didn't deserialize as much as it serialized
*There was no message handler for a message id, so the reader wasn't read until the end.
UnityEngine.Debug:LogError (object)
Mirror.NetworkServer:OnTransportData (int,System.ArraySegment`1<byte>,int) (at Assets/Mirror/Core/NetworkServer.cs:867)
Mirror.ThreadedTransport:ServerEarlyUpdate () (at Assets/Mirror/Transports/Threaded/ThreadedTransport.cs:611)
Mirror.NetworkServer:NetworkEarlyUpdate () (at Assets/Mirror/Core/NetworkServer.cs:2053)
Mirror.NetworkLoop:NetworkEarlyUpdate () (at Assets/Mirror/Core/NetworkLoop.cs:192)
#

Here is my room change code:

IEnumerator SendPlayerToNewScene(NetworkConnectionToClient conn, ServerRoomInfo room)
{
    if (conn == null || conn.identity == null) yield break;

    GameObject player = conn.identity.gameObject;
    ServerRoomInfo currentRoom = _rooms.FirstOrDefault(x => x.Scene == player.scene);
    if (currentRoom != null)
    {
        currentRoom.Players.Remove(conn.connectionId);
        ServerPlayerLeftRoom(currentRoom);
    }

    room.Players.Add(conn.connectionId);

    // Tell client to unload previous subscene with custom handling (see NetworkManager::OnClientChangeScene).
    conn.Send(new SceneMessage { sceneName = player.scene.path, sceneOperation = SceneOperation.UnloadAdditive, customHandling = true });

    // wait for fader to complete.
    yield return new WaitForSeconds((NetworkManager.singleton as EBRNetworkManager).FadeInOut.GetFadeInTime());

    // If the conn disconnected, player will be null
    if (player == null) yield break;

    // Remove player after fader has completed
    NetworkServer.RemovePlayerForConnection(conn, RemovePlayerOptions.Unspawn);

    // Remove player owned objects
    conn.DestroyOwnedObjects();

    // yield a frame allowing interest management to update
    // and all spawned objects to be destroyed on client
    yield return null;

    // reposition player on server and client
    //AvailableRoom availableRoom = _loadedDragRacingRooms[room.Index];
    MirrorPlayerSetup playerSetup = MirrorPlayerSetup.GetInstance(room.Scene);
    if(playerSetup != null) playerSetup.Teleport(player);

    // Move player to new subscene.
    //Debug.Log($"Moving player {player.name} to scene {room.RoomId} path {room.Scene.name}");
    SceneManager.MoveGameObjectToScene(player, room.Scene);

    // Tell client to load the new subscene with custom handling (see NetworkManager::OnClientChangeScene).
    conn.Send(new SceneIndexMessage { index = (byte)room.Index });
    conn.Send(new SceneMessage { sceneName = room.Scene.path, sceneOperation = SceneOperation.LoadAdditive, customHandling = true });

    // Player will be spawned after destination scene is loaded
    if (!NetworkServer.AddPlayerForConnection(conn, player))
    { 
        NetworkServer.Destroy(player);
        yield break;
    }

    // Remove from switching list
    _playersSwitchingRoom.Remove(conn.connectionId);
    // host client playerController would have been disabled by OnTriggerEnter above
    // Remote client players are respawned with playerController already enabled
    //if (NetworkClient.localPlayer != null && NetworkClient.localPlayer.TryGetComponent(out Common.Controllers.Player.PlayerControllerBase playerController))
    //    playerController.enabled = true;
}```
sullen shore
#

can't repro in unity 6.2 on linux @regal olive
the resident going up is probably just fragmentation?
both with a connected player and just an idle server