Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Resolved Getting client's parameters on disconnect

Discussion in 'Netcode for GameObjects' started by GuirieSanchez, Nov 13, 2022.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    355
    Hi, I want to get some parameters from the client side on disconnection (for whatever cause, maybe he lost connection, maybe he quit). The parameters I want to pass into the server are stored on the client's scriptable object. I tried using
    public override void OnNetworkDespawn()
    , but it's triggering too late, so the code inside is not reaching the [ServerRpc].
    I also tried the
    private void OnClientDisconnect(ulong clientId)
    callback, but only gets triggered on the Host/Server, so there's no way for me to pass in the data before the client disconnects.

    Does anyone have any idea on how to accomplish this?
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    2,071
    You have to do that BEFORE the client disconnected.

    You gave the best example yourself: "lost connection". So the client got disconnected because someone pulled the plug on the client's router. How do you think that client is supposed to send data to the server? It simply cannot.

    If you need to persist state, you have to either allow the players to manually persist it, or send it to the server at certain points in time (eg "autosave", for example on level completion or on a timer) and if it's an MMO style game you have to continuously update the player's persistant data, be it kill count, world position, hitpoints. Though you may relax some parameters to update only every couple seconds, such as world position not needing to be persisted 50 times per second, it suffices to do so every 1-5 seconds.

    Moreover, MMO style would actually keep the player in the world on the server-side even if the client's cable got pulled, so the server keeps on simulating the world with that client not doing anything and teammates wondering. This usually leads to the avatar's death when getting disconnected during combat.
     
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    355
    I understand. In my case, I guess I can pass the parameters/data back to the server at the beginning (OnClientConnect) since it is data that will remain immutable throughout.

    The reason I didn't do this is because I still don't know how to assign data to clients on the server side. I believe it should be somehow basic, but I didn't come up with anything useful. I haven't tried it, but I guess I could do something like: "OnClientConnect" I assign the data to that client, and then send a ServerRpc to give that data.

    The problem would be: How on Earth can I store this data on the server, do I use the client's
    ulong
    that is passed in on the "OnClientConnect" callback? But if so, I don't know how to do something like "this ulong gets this data". And then "OnClientDisconnect" compare the ulong against something to know which client is disconnecting, and therefore do some action with its data (already available on the server side).
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    2,071
    Btw it also triggers on the client. You probably have that hooked up incorrectly.

    Yes, you would use that ulong clientId for identification while the client is connected (including disconnect callback on server side).

    If you need custom identification, such as making sure a player that disconnects and joins again is recognized as a previous (specific) player, then you'd have to find a way for that client to generate a unique identifier.

    This could be an automatically generated GUID in its simplest form that gets sent to the server on connection approval and that GUID becomes the key for the Dictionary<GUID, PlayerData> the server uses to store client data. That dictionary could of course also be a database interface or some other way of persisting data.

    Alternatively it could be a user account, usually email and (salted, hashed) password.
     
  5. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    355

    Could I use a dictionary for this?
    For instance, "OnConnectionApproval" I have this line

    Code (CSharp):
    1.  
    2. var clientId = request.ClientNetworkId;
    3. Net.LogInfo($"=> OnConnectionApproval({clientId})");
    I guess I could then add this
    clientId
    to a dictionary and assign a custom data (in my case I want it to be an integer withdrawn from a list). I'm not sure though what's the proper way to compare this dictionary's clientId to the "OnClientDisconnect's ulong clientId" on disconnection. Maybe something like

    Code (CSharp):
    1.  
    2. private void OnClientDisconnected(ulong clientId)
    3. {
    4.      foreach (string key in (myDictionary))
    5.      {
    6.          if (clientId == myDictionary.Key)
    7.          {
    8.               //then use something like
    9.               "myDictionary.Key.GetValue"
    10.               // I haven't used dictionaries yet so this is just
    11.               // a pseudocode
    12.            }
    13.  
    14.      }
    15. }
    So, only 2 things:
    (1) is this a proper way to do it? Or is there a simpler way? Also, if I wanted to assign and store more than one parameter, I believe a dictionary is not the way to go

    (2) this is regarding:
    I don't know what I might have done wrong, the setup is simple, I have a NetworkBehaviour that subscribes to the callbacks on start:
    Code (CSharp):
    1. private void AddNetworkManagerCallbacks()
    2.         {
    3.             var netMan = NetworkManager.Singleton;
    4.             if (netMan != null)
    5.             {
    6.                 Debug.Log("RegisteringCALLBACKS");
    7.                 // ensure we never register callbacks twice
    8.                 RemoveNetworkManagerCallbacks();
    9.  
    10.                 netMan.OnClientConnectedCallback += OnClientConnected;
    11.                 netMan.OnClientDisconnectCallback += OnClientDisconnect;
    12.             }
    13.         }
    14.  
    15.         private void RemoveNetworkManagerCallbacks()
    16.         {
    17.             var netMan = NetworkManager.Singleton;
    18.             if (netMan != null)
    19.             {
    20.                 netMan.OnClientConnectedCallback -= OnClientConnected;
    21.                 netMan.OnClientDisconnectCallback -= OnClientDisconnect;
    22.             }
    23.         }
    And then, this is the "OnClientDisconnect" callback:

    Code (CSharp):
    1. private void OnClientDisconnect(ulong clientId)
    2.         {
    3.  
    4.             if (IsServer) return;
    5.             Debug.LogWarning($"OwnerClientId({OwnerClientId}) [clientId({clientId})] IS DISCONNECTING and giving back its ID");
    6.             ReturningNetworkIDToList_ServerRpc(networkPlayerID - 1);
    7.         }
     
  6. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    2,071
    In the Start() method? Maybe try OnEnable, Awake or OnNetworkSpawn. Netcode mentions the execution order somewhere in the docs, when the callbacks run and how the order is different between client and server and I can't remember it right now.

    NetworkManager.Singleton may be null when called at the wrong time (ie too early).

    As to the Dictionary: yes, key is the ulong clientId but keep in mind that it will change for a client that disconnects, then reconnects. And to store more than one value, just make a class or struct PlayerData that has all the fields you need to persist and use that as the Dictionary value.
     
  7. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    355
    On your project, the "NetcodeBootstrap" script subscribes to this on Start, I assume to give the NetworkManager.Singleton time to be created and therefore not to be null. In fact, I tried this on Awake and it never subscribed.
     
  8. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    355
    Great, thanks for the info! I appreciate your help. I'll be studying and testing dictionaries with structs.

    hmm, hard to do something about it since the "OnClientDisconnect" callback only gives that ulong that will change every time :confused:
     
  9. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    2,071
    Hmmm ... on second thought ... of course, Netcode mentions this in the docs too: you don't get events for things that were initiated by the local instance. So a client connecting is something the client initiated and thus it may not receive OnClientConnected for itself, only for other clients. I know I ran into this with OnClientDisconnected.

    That's why I made this class for receiving disconnect events from the UI buttons that kick/disconnect clients:
    https://github.com/CodeSmile-000001...cripts/Connection/NetworkDisconnectManager.cs
     
    GuirieSanchez likes this.