Search Unity

NetworkManager callbacks

Discussion in 'Multiplayer' started by cmersereau, Nov 2, 2022.

  1. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    I need help understanding the callbacks on the NetworkManager for players connecting and disconnecting to a client hosted server.

    I want to be able to detect, at least from the client-host device, when other players join and leave the server. I have a class that looks very similar to the client notification manager shown at the bottom of this page. I am not experiencing the behavior I would expect from those callbacks though.

    OnClientConnectedCallback:
    I am only seeing this event fired on the client device when connecting to the server. This includes the host when it connects as well, but the event does not fire when other clients connect to the server. It may be possible that this is all this event is intended for, but it would be inconsistent with how the disconnect works.

    OnClientDisconnectCallback:
    I can see a disconnect event from everyone when any client disconnects. This means I can see players leave when I am the host, and I can also see the host leave when I am a client. This appears to be what I want it to be, but is inconsistent with the connection callback which does not fire an event on the host device for every client. I do experience an undesired delay before the event fires (about 10 seconds).

    I tried following this documentation does not appear to be accurate, as I tried inheriting from NetworkManager and none of the state methods were available to override.

    I am using Unity 2020.3.20.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,887
    OnClientConnected should run for every client on the server side.
    could you post the code how (and when) you assign methods to the callback, and the callback method itself?
     
  3. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    @CodeSmile This is the code for the class. As I said, it's very similar to the example on the website so I'm not sure what you can glean from it. My primary way of checking if the events are firing are the log statements you see. There is very little actual behavior attached to the event as we are testing the flow before cementing UX.

    It might be worth mentioning that the client connection is made in one scene and then the host is moved to a new scene, using the network scene manager, to await a second player. That is the reason for the subscribed check.

    Code (CSharp):
    1. public class ClientConnectionMonitor : MonoBehaviour
    2.     {
    3.         /// <summary>
    4.         /// This action is invoked whenever a client connects, passing the new client's ID
    5.         /// </summary>
    6.         public UnityEvent<ulong> OnClientConnected;
    7.  
    8.         /// <summary>
    9.         /// This action is invoked whenever a client disconnects, passing the exiting client's ID
    10.         /// </summary>
    11.         public UnityEvent<ulong> OnClientDisconnected;
    12.  
    13.         private bool _isSubscribed = false;
    14.  
    15.         private static ClientConnectionMonitor _instance;
    16.         public static ClientConnectionMonitor Instance => _instance;
    17.  
    18.  
    19.         protected void Awake()
    20.         {
    21.             if (Instance == null)
    22.             {
    23.                 DontDestroyOnLoad(this);
    24.                 _instance = this;
    25.             }
    26.         }
    27.  
    28.         private void Start()
    29.         {
    30.             if (NetworkManager.Singleton == null)
    31.             {
    32.                 // Can't listen to something that doesn't exist >:(
    33.                 throw new Exception(
    34.                     $"There is no {nameof(NetworkManager)} for the {nameof(ClientConnectionMonitor)} to subscribe to. " +
    35.                     $"Please add a {nameof(NetworkManager)} to the scene.");
    36.             }
    37.  
    38.             if (_isSubscribed)
    39.             {
    40.                 return;
    41.             }
    42.  
    43.             NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnectedCallback;
    44.             NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback;
    45.             _isSubscribed = true;
    46.         }
    47.  
    48.         protected void OnDestroy()
    49.         {
    50.             if (NetworkManager.Singleton != null && _isSubscribed)
    51.             {
    52.                 NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnectedCallback;
    53.                 NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback;
    54.                 _isSubscribed = false;
    55.             }
    56.         }
    57.  
    58.         private void OnClientConnectedCallback(ulong clientId)
    59.         {
    60.             Debug.Log($"{this.Prefix()} client connected: {clientId}");
    61.             OnClientConnected?.Invoke(clientId);
    62.         }
    63.  
    64.         private void OnClientDisconnectCallback(ulong clientId)
    65.         {
    66.             Debug.Log($"{this.Prefix()} client disconnected: {clientId}");
    67.             OnClientDisconnected?.Invoke(clientId);
    68.         }
    69.     }
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,887
    This is how I do the callbacks, maybe you can spot a difference: https://github.com/CodeSmile-000001...dAssets/Scripts/Bootstrap/NetcodeBootstrap.cs

    I find the isSubscribed flag odd, it isn‘t necessary and just adds complexity,
    Also the fact that you call DontDestroyOnLoad. The docs state that the example script should be attached to the NetworkManager object itself, thus it would already be in DontDestroyOnLoad. Maybe that is an issue?
     
    alysonlupo likes this.
  5. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    I add DontDestroyOnLoad for coding consistency. The fact that it's on a game object where other scripts make that same call doesn't indicate to me that this script shouldn't take care of itself. It is something that shouldn't be destroyed on load, so it should call that function. I do have it attached to the same object as my NetworkManager, and I can't say that I see any issues with it. If there was a problem, it seems like an error would be thrown or the other callbacks wouldn't work, but they do. It's only the one callback in the one circumstance.

    As for the isSubscribed flag, I noticed for behaviours that don't get destroyed that the Start method was getting called after scene changes, so it does make sense to me to have that check. Either way, it couldn't be the issue as I still get the disconnect event from the host device so the callbacks are definitely connected.

    It looks to me like we are doing the same thing. The only potential difference is the scene change I perform to pull players into my multiplayer scene.
     
  6. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I tested your code and OnClientConnectedCallback is triggered when a client connects so it appears the problem is elsewhere.

    Just to clarify OnClientDisconnectCallback, this is triggered on the client when they're disconnected by the server rather than when the host disconnects, although it amounts to the same thing. If you're running server-only with no host the client will still receive this event.
     
  7. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    Thanks for testing it. I'm not sure if I should be encouraged or discouraged by the fact that it works for you. Currently I can't think of any reason why the event wouldn't be fired. As you can see from the way I'm logging a message right off of the network manager event, if the event was fired at all I'd definitely see something for it. I can't imagine what could stop the network manager from firing the event for this.
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    It worked apart from this.Prefix(), what is that referring to?

    You could try testing that class in isolation in a new project, if it does then try and work out what your main project is doing differently.
     
  9. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    this.Prefix() is an extension method we use to easily log the class and function name. I'll probably try that soon if I can't find anything out.
     
  10. cmersereau

    cmersereau

    Joined:
    Nov 6, 2020
    Posts:
    52
    So we've figured out what was causing the issue. We are working on a VR game, and to test multiplayer features I would build a copy of the game and install it on my headset and then also run a version in my editor, so as to not take time from another developer. What we've learned is that there is an issue for our project running between an editor and a built version of the game.

    Here's a breakdown of combinations that we tried:
    • Windows and Windows - works
    • Android and Android - works
    • Editor and Editor - works
    • Windows and Android - works
    • Editor and Windows - doesn't work
    • Editor and Android - doesn't work
    And by doesn't work, I mean that we saw object hash errors and a failure to receive the OnClientConnectedCallback event. It's very possible that this isn't a restriction of the network manager, but of other elements of our project. We use addressables and it may be that the hash error refers to that, but we would need to test in an isolated project that doesn't use addressables to know that for sure.

    Thanks @cerestorm and @CodeSmile for taking a look at this with me.
     
    Last edited: Nov 7, 2022
    cerestorm likes this.