Unity Netcode disconnects after reconnecting with 80 seconds delay

Hi there,

we currently have an issue with our app, that when we try to reconnect one of our clients using Unity.Netcode, it does work in the beginning, but after a delay of about 80 seconds we get another message on the Clientside, that the connection to the server failed and it tries to shut down.

The thing is, we have a script for rejoining and already shut down the Network manager and restarted the scene. As seen below, this message triggers another shutdown AFTER that, which weirdly only stops the Client from seeing the movement, triggered by the host, but that should not happen.

Where is this Message/Connectionerror coming from and why the 80 seconds delay and where can we stop this from happening?

Best Regards
Florian Buchholz

2024/11/04 13:31:56.067 5138 5160 Error Unity Failed to connect to server.
2024/11/04 13:31:56.067 5138 5160 Error Unity Unity.Netcode.Transports.UTP.UnityTransport:ProcessEvent()
2024/11/04 13:31:56.067 5138 5160 Error Unity Unity.Netcode.Transports.UTP.UnityTransport:Update()
2024/11/04 13:31:56.067 5138 5160 Error Unity 
2024/11/04 13:31:56.068 5138 5160 Info Unity [Netcode] Disconnect Event From 0
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.NetworkConnectionManager:DisconnectEventHandler(UInt64)
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.Transports.UTP.UnityTransport:ProcessEvent()
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.Transports.UTP.UnityTransport:Update()
2024/11/04 13:31:56.068 5138 5160 Info Unity 
2024/11/04 13:31:56.068 5138 5160 Info Unity [Netcode] Shutdown
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.NetworkManager:Shutdown(Boolean)
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.NetworkConnectionManager:DisconnectEventHandler(UInt64)
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.Transports.UTP.UnityTransport:ProcessEvent()
2024/11/04 13:31:56.068 5138 5160 Info Unity Unity.Netcode.Transports.UTP.UnityTransport:Update()

Can you post the code that does that?

If you
a) destroy the NetworkManager component or object or
b) call NetworkManager shutdown and instantly call StartClient again
c) cause DDOL object duplication or singleton instances to be recreated when switching scenes

you will - almost guaranteed - have all sorts of issues.

We fixed the issue. Our NetworkDiscovery, that chooses between Client and Server, was not a Singleton and got recreated on restarting the Scene. That lead to other issues. We also didn’t wait long enough for the Client to restart at some point, since we started the “Whileshutdown” before the shutdown even was triggered automatically.

Thanks for your answer.

Btw, this is the current Reconnect-Manager:

using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
using Unity.Netcode;

public class ReconnectManager : MonoBehaviour
{
    public static ReconnectManager Instance;

    private float _waitTimeBeforeRestart = 10f; // Time to wait before restarting the scene

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // Persist across scene reloads
        }
        else
        {
            Destroy(gameObject); // Enforce singleton behavior
        }
    }

    private void Start()
    {
        // Register for the disconnect event
        if (NetworkManager.Singleton != null)
        {
            NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;
        }
    }

    private void OnDestroy()
    {
        // Unregister the disconnect event to prevent issues when the manager is destroyed
        if (NetworkManager.Singleton != null)
        {
            NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnected;
        }
    }

    // This method is triggered when the player disconnects
    private void OnClientDisconnected(ulong clientId)
    {
        if (clientId == NetworkManager.Singleton.LocalClientId)
        {
            Debug.LogError("Client disconnected. Waiting to restart the scene...");
            StartCoroutine(RestartSceneAfterDelay());
        }
    }

    // Coroutine to wait and then restart the scene
    private IEnumerator RestartSceneAfterDelay()
    {
        yield return new WaitForSeconds(5);
        while (NetworkManager.Singleton.ShutdownInProgress) yield return null;


        var disconnectReason = NetworkManager.Singleton.DisconnectReason;

        if (string.IsNullOrEmpty(disconnectReason))
        {
            Reconnect();
        }
        else
        {
            Debug.LogError($"Disconnected because of reason: {disconnectReason}");

        }


    }

    private void Reconnect()
    {

        Debug.LogError($"Trying to reconnect");

        // Restart the current scene
        Debug.LogError("Restarting the scene...");
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);

            NetworkDiscovery.Singleton.StartListening(); 

    }

}
1 Like

I know this singleton pattern is spread wide and far, but it’s potential for issues I keep pointing out. Imagine that this object DOES get created again. Sure, the singleton will not be replaced. But what if the same object has another script on it, whose Awake perhaps changes a static field somewhere … this can be a really, really nasty bug to figure out!

At the minimum, make sure no such singleton has any other scripts on the singleton gameobject.

Also potential bug here. What happens if, for whatever reason, NetworkManager happens to still be null when Start() runs?

Suddenly, some builds/clients will not receive OnClientDisconnected events.
In such cases it’s preferable to allow the code to run into the NullreferenceException rather than silently skip the code.
It’s okay in OnDestroy of course because most of the time, NetworkManager will be null and thus the event is already unregistered.

This is just a guess that “5 seconds will be enough”. But sometime in the future you’ll get this flagged as “scene reload takes too long” and then you may reduce it to 1 second, or even 0.1 second. Where’s the sweet spot? You don’t know and there’s also no guarantee because you’re waiting for realtime seconds, not frames.

Instead, you really only need to make this test to 100% guarantee that the NetworkManager has fully shut down:

var n =NetworkManager.Singleton;
if (false == (n.IsServer && n.IsHost && n.IsClient && n.IsListening && n.ShutdownInProgress))
   // can restart network now ...

Note that testing for ShutdownInProgress is NOT sufficient. The first three flags are the last to get reset back to false in my observations, whereas ShutdownInProgress is barely even true - it kinda makes me think that this flag should not be exposed but is rather only used internally.

The full shutdown usually happens within a frame for clients, and for the server (host!) it may take several frames as the server is waiting for remaining incoming “client disconnected” messages before it fully shuts down. Worst case, the server may wait for a client timeout to occur if it so happens that a client crashes just when the server ends the session.