Search Unity

Resolved Why am I getting 2 Network Manager objects?

Discussion in 'Netcode for GameObjects' started by alexogorek, Jun 25, 2022.

  1. alexogorek

    alexogorek

    Joined:
    Jun 21, 2018
    Posts:
    18
    https://docs-multiplayer.unity3d.com/netcode/current/components/networkmanager 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:
    upload_2022-6-24_22-56-44.png

    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)
    upload_2022-6-24_22-57-14.png

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

    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)
     

    Attached Files:

  2. alexogorek

    alexogorek

    Joined:
    Jun 21, 2018
    Posts:
    18
    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.

    upload_2022-6-24_23-28-58.png

    upload_2022-6-24_23-29-24.png
     
  3. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    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.
     
    Sivue, Volshar and alexogorek like this.
  4. alexennerfelt

    alexennerfelt

    Joined:
    Jan 7, 2017
    Posts:
    15
    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:
    Code (CSharp):
    1. if(NetworkManager.Singleton != null){
    2. //Destroy the extra instance
    3. }
    Or simply use additive scene loading so you don't load any additional instances.

    Hope this help, have a great weekend :)
     
    alexogorek likes this.
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    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:

    Code (CSharp):
    1.         private void AddNetworkManagerCallbacks()
    2.         {
    3.             var netMan = NetworkManager.Singleton;
    4.             if (netMan != null)
    5.             {
    6.                 // ensure that we never register twice in case this method is called more than once
    7.                 RemoveNetworkManagerCallbacks();
    8.  
    9.                 netMan.OnServerStarted += OnServerStarted;
    10.                 netMan.OnClientConnectedCallback += OnClientConnected;
    11.                 netMan.OnClientDisconnectCallback += OnClientDisconnected;
    12.             }
    13.         }
    14.  
    15.         private void RemoveNetworkManagerCallbacks()
    16.         {
    17.             var netMan = NetworkManager.Singleton;
    18.             if (netMan != null)
    19.             {
    20.                 netMan.OnServerStarted -= OnServerStarted;
    21.                 netMan.OnClientConnectedCallback -= OnClientConnected;
    22.                 netMan.OnClientDisconnectCallback -= OnClientDisconnected;
    23.             }
    24.         }
     
  6. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    This happened to me too, just delete network manager when disconnecting:

    NetworkManager.Shutdown();
    NetworkManager networkManager = GameObject.FindObjectOfType<NetworkManager>();
    Destroy(networkManager.gameObject);
     
  7. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    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! ;)
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    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.
     
    lavagoatGG likes this.
  9. pradotech

    pradotech

    Joined:
    Oct 17, 2019
    Posts:
    35
    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():
    Code (CSharp):
    1. if (Singleton == null)
    2. {
    3.      SetSingleton();
    4. }
    Shouldn't be something like?
    Code (CSharp):
    1. if (Singleton == null)
    2. {
    3.      SetSingleton();
    4. }
    5. else
    6.      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:
    Code (CSharp):
    1.     private IEnumerator Start()
    2.     {
    3.         while(NetworkManager.Singleton == null)
    4.             yield return null;
    5.  
    6.         if (GetComponent<NetworkManager>() != NetworkManager.Singleton)
    7.         {
    8.             Debug.Log("Destroying this clone", gameObject);
    9.             Destroy(gameObject, 5f);
    10.         }
    11.     }
    The logging and the delayed destroy are only for testing purposes, but it seems to be working for now.
     
    Last edited: Oct 12, 2022
  10. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    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
     
  11. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    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).
     
  12. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    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.
     
  13. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    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);
     
  14. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    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?
     
  15. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    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).
     
  16. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    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.
     
    lavagoatGG likes this.
  17. Shack_Man

    Shack_Man

    Joined:
    Jun 7, 2017
    Posts:
    372
    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.
     
  18. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    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.
     
  19. Shack_Man

    Shack_Man

    Joined:
    Jun 7, 2017
    Posts:
    372
    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?
     
  20. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    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.
     
    CodeSmile likes this.
  21. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    It‘s really just a scene to launch the game with that has the NetworkManager object in it, and a script that calls LoadScene (not networked!) in Update so it will switch to the actual first scene within the first frame. You won‘t notice the delay and you can always return to the actual first scene without having to worry about NetworkManager duplicated because it only exists in the „launch scene“.
     
  22. Shack_Man

    Shack_Man

    Joined:
    Jun 7, 2017
    Posts:
    372
    Maybe I wasn't clear enough or misunderstand the replies: My issue is that I want to be working in the editor on the actual game scene that uses networking, and I want to hit play in the editor many times to test out script changes. I structure my code so that every scene can stand on it's own, so I can just keep working in one scene and don't have to switch to the loading scene in the editor and hit play there to test out the game scene.

    I understand the concept of loading scenes, and I don't like it. So my question is rather if there is a better way, like a scene manager that can check whenever I hit play if the loading scene has already loaded and if not, load it additively and then load the scene I was hitting play in.
     
  23. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    In that case make use of additive scene loading, but only load the scene when in editor and playmode.
     
  24. rojenarda

    rojenarda

    Joined:
    Jun 7, 2023
    Posts:
    2
    I have a similar problem, I use a cleanup scene that destroys network manager (and other singletons I use) when returning to main menu scene, where all these singletons are being initialized. Whenever a players leaves a lobby or the game, they load the clenup scene first, then it redirects to the main menu scene. Leaving sequence works as follows:
    1. Hit leave button, call NetworkManager.Singleton.Shutdown
    2. Load cleanup scene, if NetworkManager.Singleton is not null, destroy its game object
    3. Load main menu scene, create network manager game object and other singletons
    The problem is that after this if I try to use NetworkManager again, it throws NullReferenceException, but NetworkManager gameobject is present in the scene and it is enabled, am I doing something wrong? Should I not call Shutdown when a player leaves the lobby or game? Or is there something I should do other than spawning the NetworkManager game object to initialize NetworkManager (although it is initialized correctly on the first time)
     
  25. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,892
    Have an Init scene with NetworkManager and make sure it's the first scene in the build settings list. The only thing it does is load the actual first scene and contain the NetworkManager.

    From then on you don't have to worry about NetworkManager duplicating because the scene it is in will never ever be loaded again.