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.
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?
@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): public class ClientConnectionMonitor : MonoBehaviour { /// <summary> /// This action is invoked whenever a client connects, passing the new client's ID /// </summary> public UnityEvent<ulong> OnClientConnected; /// <summary> /// This action is invoked whenever a client disconnects, passing the exiting client's ID /// </summary> public UnityEvent<ulong> OnClientDisconnected; private bool _isSubscribed = false; private static ClientConnectionMonitor _instance; public static ClientConnectionMonitor Instance => _instance; protected void Awake() { if (Instance == null) { DontDestroyOnLoad(this); _instance = this; } } private void Start() { if (NetworkManager.Singleton == null) { // Can't listen to something that doesn't exist >:( throw new Exception( $"There is no {nameof(NetworkManager)} for the {nameof(ClientConnectionMonitor)} to subscribe to. " + $"Please add a {nameof(NetworkManager)} to the scene."); } if (_isSubscribed) { return; } NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnectedCallback; NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback; _isSubscribed = true; } protected void OnDestroy() { if (NetworkManager.Singleton != null && _isSubscribed) { NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnectedCallback; NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback; _isSubscribed = false; } } private void OnClientConnectedCallback(ulong clientId) { Debug.Log($"{this.Prefix()} client connected: {clientId}"); OnClientConnected?.Invoke(clientId); } private void OnClientDisconnectCallback(ulong clientId) { Debug.Log($"{this.Prefix()} client disconnected: {clientId}"); OnClientDisconnected?.Invoke(clientId); } }
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?
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.
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.
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.
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.
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.
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.