Unclear how to use PlayerPrefabHash in Connection Approval

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:

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!

1 Like

Hi @ledbetterMRI , I forwarded your question to the NGO team, I’ll post an answer here as soon as I get it!

Thank you! Looking forward to it.

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:

  • Find the alternate player prefab

  • Select it so that it shows up in the inspector (not opening it but just selecting it)

  • In the inspector view, scroll down to the alternate player prefab’s NetworkObject

  • Right click on the GlobalObjectIdHash value and select copy.

  • The value copied should be added/pasted to whatever component/script that handles the connection approval process

  • If it is just one possible alternate player prefab, then a single unsigned integer property/field will do.

  • 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:

private void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
{
    // The client identifier to be authenticated
    var clientId = request.ClientNetworkId;

    // Additional connection data defined by user code
    var connectionData = request.Payload;

    // Your approval logic determines the following values
    response.Approved = true;
    response.CreatePlayerObject = true;

    // The prefab hash value of the NetworkPrefab, if null the default NetworkManager player prefab is used
    response.PlayerPrefabHash = null;  //<-----(assign unique player prefab's GlobalObjectIdHash value here)

    // Position to spawn the player object (if null it uses default of Vector3.zero)
    response.Position = Vector3.zero;

    // Rotation to spawn the player object (if null it uses the default of Quaternion.identity)
    response.Rotation = Quaternion.identity;

    // If additional approval steps are needed, set this to true until the additional steps are complete
    // once it transitions from true to false the connection approval response will be processed.
    response.Pending = false;
}

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!

2 Likes

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.

1 Like

Happy to help! I’ll also forward your latest feedback to the dev team, so they’ll know that this is a requested feature.

1 Like

I am looking forward to this

Quick update: this PR that just got merged should make this possible

1 Like

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?