Why am I getting 2 Network Manager objects?

NetworkManager | Unity Multiplayer Networking is showing that it’s a Singleton (and I can see that through the DontDestroyOnLoad in the hierarchy)

This is my current layout:
MenuScene
InGameScene

MenuScene has a minimal hierarchy:

InGameScene does not have a network manager in here (notice the search in the top left to prove nothing in this scene has that component)

Yet… When I go
MenuScene > InGameScene > MenuScene via the NetworkManager scene management, it seems to cause 2 instances of NetworkManager, which is causing problems :confused:

Here’s a video showing this behavior:

Here is the code I am using to transition between scenes:
// (in the menu’s button action):
NetworkManager.Singleton.SceneManager.LoadScene(“InGame”, UnityEngine.SceneManagement.LoadSceneMode.Single);

// (in the settings button action):
NetworkManager.Singleton.SceneManager.LoadScene(“MainMenu”, UnityEngine.SceneManagement.LoadSceneMode.Single);

and as far as i can tell, this would trigger the host to move back to the main menu (which it does), but surprisingly the network manager is duplicated (which causes other issues when trying to start a new game because it runs into issues)



Although I guess fwiw – it seems even the BossRoom sample has a “startup” scene, and never returns to that scene (thereby never running into the scenario i described of navigating to a scene which was previously visited and contains a network manager object)

So maybe i’ll try that approach to see how it feels.

Having a startup scene is indeed the way to go, since you never want to return to a scene where the NetworkManager is a scene object to avoid scenarios like this.

3 Likes

It’s becuase you load the scene with the Main Menu that has the instance of the NetworkManager twice (once on play, and once when exiting InGame). Becuase of this, another instance will be put in DontDestroy and you’re stuck with two.

Try doing a:

if(NetworkManager.Singleton != null){
//Destroy the extra instance
}

Or simply use additive scene loading so you don’t load any additional instances.

Hope this help, have a great weekend :slight_smile:

1 Like

Just to add to this, another cause for seeing additional NetworkManager objects in DontDestroyOnLoad is when you did not correctly unsubscribe all of the objects subscribed to NetworkManager event handlers. It is best practice to have a pair of RegisterEvents and UnregisterEvents methods and the RegisterEvents method also calls UnregisterEvents first to be absolutely sure you don’t register twice.

Still registered event handlers will then cause additional NetworkManager to appear when changing scenes, even after NetworkManager has been shut down and you load the scene with the non-networked SceneManager.LoadScene().

Example event handler pair:

        private void AddNetworkManagerCallbacks()
        {
            var netMan = NetworkManager.Singleton;
            if (netMan != null)
            {
                // ensure that we never register twice in case this method is called more than once
                RemoveNetworkManagerCallbacks();

                netMan.OnServerStarted += OnServerStarted;
                netMan.OnClientConnectedCallback += OnClientConnected;
                netMan.OnClientDisconnectCallback += OnClientDisconnected;
            }
        }

        private void RemoveNetworkManagerCallbacks()
        {
            var netMan = NetworkManager.Singleton;
            if (netMan != null)
            {
                netMan.OnServerStarted -= OnServerStarted;
                netMan.OnClientConnectedCallback -= OnClientConnected;
                netMan.OnClientDisconnectCallback -= OnClientDisconnected;
            }
        }

This happened to me too, just delete network manager when disconnecting:

NetworkManager.Shutdown();
NetworkManager networkManager = GameObject.FindObjectOfType();
Destroy(networkManager.gameObject);

Yes, I know, and I manually deleted it from the scene just to see that it’s safe in my environment to do so.

However, this isn’t a solution, and certainly not one I would advertise. Primarily because with your code snippet, there is NO guarantee that it will get and destroy the duplicate. It might as well destroy the original, breaking Netcode. And it may even occur only on some clients, or some builds - is FindObjectOfType deterministic? I don’t know, and I don’t want to find out that way!

The actual solution if you get NetworkManager duplication is to find out what’s causing the duplicate. And then fix that! :wink:

I think lavagoat is deleting it before switching back to the scene containing the network manager. The example I gave using that code was called in the OnClientDisconnectCallback where it might be safe to use, if the network manager has cleaned itself up by that point.

I had a look at the underlying code and the potential issue I can see with calling Shutdown, deleting, then switching scenes is that shutting down is a two step process, with the Shutdown call just flagging for shutdown, then the actual shutdown takes place in OnNetworkPostLateUpdate. The reason it doesn’t cause a problem is that calling Destroy doesn’t destroy the object straight away and there appears to be enough time for the network manager to do its clean up before the scenes are switched. I did try to get it break in testing but had no luck. It does break in my main project but that’s likely due to other issues.

I’m not sure I’d be comfortable deleting this way and would probably opt for switching to a scene after the network manager scene. You might be able to get away with deleting the network manager but I would wait for ShutdownInProgress to become false first.

2 Likes

I do have the same problem. My NetworkManager is placed in my MainMenu scene, and everytime I switch back from GameScene to MainMenu, another NetworkManager spawns. I’m handling events subscription and unsubscription correctly as said above, but I think the issue is in the way NetworkManager handles his Singleton.

Inside NetworkManager OnEnable():

if (Singleton == null)
{
     SetSingleton();
}

Shouldn’t be something like?

if (Singleton == null)
{
     SetSingleton();
}
else
     Destroy(this);

I’m not an experienced dev at all, but I hope this makes sense.

Edit: As a temporary workaround I placed this script attached to the same game object as NetworkManager:

    private IEnumerator Start()
    {
        while(NetworkManager.Singleton == null)
            yield return null;

        if (GetComponent<NetworkManager>() != NetworkManager.Singleton)
        {
            Debug.Log("Destroying this clone", gameObject);
            Destroy(gameObject, 5f);
        }
    }

The logging and the delayed destroy are only for testing purposes, but it seems to be working for now.

How should I wait? writing an empty while gets unity stuck and the function I am shutting the manager down in is an async so it can’t be an enumerator

You’d need to poll in Update to check until it becomes false, this can be a bit of pain to be honest.

You’d be making your life a lot easier if you follow the advice of having a start scene where the network manager resides and never return back to that scene. Something like StartScene → MenuScene → GameScene(s). Run the game from StartScene and when the game is over return to the MenuScene.

This should also remove the need to check for Shutdown to be complete (and you won’t need to destroy the network manager).

My network manager is currently in my game scene, and whenever I come to it I get another instance. I forgot about the update method because I barely use it. Thank you.

I moved the networkManager to the menu scene but now when I try to leave the lobby I get

NullReferenceException: Object reference not set to an instance of an object

on this line:
await LobbyService.Instance.DeleteLobbyAsync(lobby.Id);

I don’t use Lobby but I don’t see how having the network manager in that scene would have any impact on it.

Is LobbyService.Instance null, if so can you see what it’s not being set?

It’s related to the lobby variable, probably an unrelated issue. Although When I tried to wait on update for the network manager to shutdown before deleting it I got an exception saying the lobby can’t be found (Although I did not use the lobby service).

I’d avoid the need to do the Update check and deleting the network manager and just put it in an earlier scene. You can try creating a new first scene just for the network manager but it sounds like it shouldn’t be necessary.

1 Like

I had the same issue, I assumed NetworkManager.Singleton would behave like a Singleton…

To keep it isolated to one scene and avoid a productivity killing loading scene I made a wrapper class that instantiates a NetworkManager from a prefab in awake and destroys it in OnDisable. 1

I’m not even calling the Shutdown() and it all works flawlessly. I’ll report back if I run into issues.

The best solution to this is to have an empty scene with just the NetworkManager in it which does nothing besides loading the actual first scene. That way the NetworkManager is already in DontDestroyOnLoad and will not be duplicated no matter what.

How would you work on a different scene? I really want to avoid working in e.g. the game scene and having to switch to the loading scene before play mode (that’s what I meant with productivity killer).
I have not used additive scene loading, could this easily solve this problem?

Add a scene in the start of your game with only the network manager and switch to your current starting scene regularly. This will make a network manager in your starting scene and no more will appear after you load it again.

1 Like