Search Unity

Resolved Unclear how to use PlayerPrefabHash in Connection Approval

Discussion in 'Netcode for GameObjects' started by ledbetterMRI, Nov 22, 2022.

  1. ledbetterMRI

    ledbetterMRI

    Joined:
    May 29, 2019
    Posts:
    25
    Network Object Documentation states: "If you want to be able to assign a unique player prefab on a per client connection basis, use client Connection Approval".

    The Connection Approval doc states: "Connection approval also enables you to specify the player prefab to be created, allowing you to override the default NetworkManager defined player prefab on a per player basis."

    This is done by setting the PlayerPrefabHash value on the response object. Unfortunately, the Connection Approval doc doesn't provide a concrete example of this and I can't find any documentation explaining how to fetch a Network Prefab's hash value through code alone. In fact, NetworkObject's GlobalObjectIdHash is set to internal, so there's no way to query it directly. Further, the old MLAPI methods for getting this hash value (GetPrefabHashFromGenerator() and GetPrefabHashFromIndex()) have been removed from Netcode 1.0+.

    Other users have run into this without satisfactory explanation:

    https://forum.unity.com/threads/how-to-obtain-playerprefabhash-from-prefabs.1352933/
    https://forum.unity.com/threads/how-to-get-prefab-hash.1256388/

    According to the last link, the only way to use this feature is to override a network prefab's hash manually on the NetworkManager component and then reference this custom hash value in our own scripts. This goes against the Unity paradigm of maintaining inspector references through assets, not bespoke IDs. This workflow is not self-documenting, making it harder to debug for developers who didn't do the initial setup. Should the hash value be misstyped or the NetworkManager's gameobject get replaced by an unsuspecting developer, this will cause spawn errors.

    I could very well be missing the point of doing it this way, but current documentation lacks the details necessary to explain the implementation as-is and how Unity suggests using it. Clarification from Unity is greatly appreciated!
     
    Talal88 likes this.
  2. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hi @ledbetterMRI , I forwarded your question to the NGO team, I'll post an answer here as soon as I get it!
     
  3. ledbetterMRI

    ledbetterMRI

    Joined:
    May 29, 2019
    Posts:
    25
    Thank you! Looking forward to it.
     
  4. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hey @ledbetterMRI , I've got an answer which I'm copy-pasting here:

    "Until we get that added to the Connection Approval page, here is what the user needs to do:
    1. Find the alternate player prefab
    2. Select it so that it shows up in the inspector (not opening it but just selecting it)
    3. In the inspector view, scroll down to the alternate player prefab's NetworkObject
    4. Right click on the GlobalObjectIdHash value and select copy.
      1. The value copied should be added/pasted to whatever component/script that handles the connection approval process
        1. If it is just one possible alternate player prefab, then a single unsigned integer property/field will do.
        2. If they are going to have many, then they might want to have a "list of GlobalObjectIdHash" values held within a property/field.
    On the connection approval page we provide a skeleton script:

    Code (CSharp):
    1. private void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
    2. {
    3.     // The client identifier to be authenticated
    4.     var clientId = request.ClientNetworkId;
    5.  
    6.     // Additional connection data defined by user code
    7.     var connectionData = request.Payload;
    8.  
    9.     // Your approval logic determines the following values
    10.     response.Approved = true;
    11.     response.CreatePlayerObject = true;
    12.  
    13.     // The prefab hash value of the NetworkPrefab, if null the default NetworkManager player prefab is used
    14.     response.PlayerPrefabHash = null;  //<-----(assign unique player prefab's GlobalObjectIdHash value here)
    15.  
    16.     // Position to spawn the player object (if null it uses default of Vector3.zero)
    17.     response.Position = Vector3.zero;
    18.  
    19.     // Rotation to spawn the player object (if null it uses the default of Quaternion.identity)
    20.     response.Rotation = Quaternion.identity;
    21.  
    22.     // If additional approval steps are needed, set this to true until the additional steps are complete
    23.     // once it transitions from true to false the connection approval response will be processed.
    24.     response.Pending = false;
    25. }
    The response.PlayerPrefabHash property is where the user should apply the alternate GlobalObjectIdHash value.
    If the player is selecting one of several characters, then the client side can send this information just prior to starting the client via the NetworkManager.NetworkConfig.ConnectionData field.

    I will put together an update to the Connection Approval documentation for this as well.

    Of course, making GlobalObjectIdHash public for reading and internal for writing (or a public accessor method) could greatly simplify this process... They could then just have a list of "GameObjects" that are the alternate player prefabs and read the GlobalObjectIdHash value from them at runtime. We might want to add a feature request for that as well. "

    Please let me know if this helps!
     
    MiTsSsS4 and ledbetterMRI like this.
  5. ledbetterMRI

    ledbetterMRI

    Joined:
    May 29, 2019
    Posts:
    25
    Brilliant, thanks for the quick response! Those steps provide a clear path to finding the ID hash in the editor and assigning them in the connection approval method.

    That said, I definitely think it's worth making the ID hash public for reading; the developer is right on the money about the simplicity of keeping a list of gameobjects assigned in the inspector as opposed to inspecting each network prefab and maintaining a list of IDs by hand. At a glance, ID values can't tell designers which prefabs are included in the list and which aren't. Should they want to, say, remove a specific prefab from the list or put the prefabs in a specific order based on some arbitrary quality, the designer has to hunt down each network prefab to compare IDs against. If you're working with a large list or one in flux, this takes up a lot of extra time and effort. Not to mention the brittle nature of human error (mistyping the ID value, etc...).

    The interim workaround I went with was to copy the logic used to generate these ID hashes (NetworkObject.cs lines 20-41 and the requisite hashing algorithm class) out of the Netcode package and use it to serialize the hashes from a list network prefabs. Adding back the ability to get hash IDs from the generator would basically do the same if there's any caveats exposing the GlobalIDHash for reading publicly.

    Again, I really appreciate you sending my feedback to the Netcode team and their swift response. My overall experience with netcode and its documentation has been great.
     
    RikuTheFuffs-U likes this.
  6. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Happy to help! I'll also forward your latest feedback to the dev team, so they'll know that this is a requested feature.
     
    ledbetterMRI likes this.
  7. azcoffeehabit

    azcoffeehabit

    Joined:
    Jun 14, 2015
    Posts:
    13
    I am looking forward to this
     
  8. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Quick update: this PR that just got merged should make this possible
     
    ledbetterMRI likes this.
  9. ANLevant

    ANLevant

    Joined:
    Jun 10, 2014
    Posts:
    28
    I'm having this issue where the game server is starting, but clients try to connect into it and he accepts the requests, resulting in errors like NetworkVariables not yet initialized and other pipeline of errors. I am aware this is happening because I'm triggering an OnClientConnect code, but it's critical for me to set up those clients on connection, injecting the server's available rules for a "Draft" phase before starting a ranked match. I thought Connection Approval would solve the issue since it required to verify a handshake between user and client, but it always ends up with a NullPointerException. Am I doing something wrong? Is there a best practice guide, or should I try fixing the connection approval flag?