Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Parenting a host NetworkObject causing soft synchronization errors on clients

Discussion in 'Netcode for GameObjects' started by MafiaMoe, Jan 28, 2023.

  1. MafiaMoe

    MafiaMoe

    Joined:
    Mar 24, 2013
    Posts:
    27
    When spawning the same network objects using the same ServerRpc on both hosts and clients, the host NetworkObject does not display on any clients, even though the clients can see each other and the host can see the clients. Also, this only occurs if the NetworkObject is parented to another NetworkObject. The image show the issue and the error message that arises. Spawning code is below as well.

    What I have checked for so far:
    The GlobalObjectIdHash is identical for both host and client spawned NetworkObjects.
    The GlobalObjectIdHash matches with the prefab NetworkObject trying to be spawned.
    Disabling the parenting code remedies the error, but the spawned NetworkObjects are left as root objects.
    The issue arises regardless of using a ServerRpc on the host or spawning natively.

    Network Host Spawn Error.png

    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Netcode;
    3.  
    4. public class CarSpawner : NetworkBehaviour
    5. {
    6.     [SerializeField] private GameObject m_CarPrefab;
    7.     [SerializeField] private NetworkObject m_SpawnedNetworkObject;
    8.     public NetworkObject LocalCarObject => m_SpawnedNetworkObject;
    9.  
    10.     public override void OnNetworkSpawn()
    11.     {
    12.         //only the server spawns
    13.         /*if (IsHost) //diabled to test if the using the same code on host and client would remedy the error
    14.         {
    15.             //instantiate the game object
    16.             GameObject instance = Instantiate(m_CarPrefab);
    17.  
    18.             //get the instances NetworkObject and Spawn
    19.             m_SpawnedNetworkObject = instance.GetComponent<NetworkObject>();
    20.             m_SpawnedNetworkObject.Spawn();
    21.             //m_SpawnedNetworkObject.TrySetParent(gameObject);
    22.         }
    23.         else if (IsClient)*/
    24.         {
    25.             //tell the server to spawn a car for us
    26.             SpawnClientCarServerRpc(NetworkManager.Singleton.LocalClientId);
    27.         }
    28.     }
    29.  
    30.     [ServerRpc(RequireOwnership = false)]
    31.     public void SpawnClientCarServerRpc(ulong clientID, ServerRpcParams rpcParams = default)
    32.     {
    33.         //instantiate the game object
    34.         GameObject instance = Instantiate(m_CarPrefab);
    35.  
    36.         //get the instances NetworkObject and Spawn
    37.         NetworkObject spawned = instance.GetComponent<NetworkObject>();
    38.         spawned.SpawnWithOwnership(clientID);
    39.         spawned.TrySetParent(gameObject);
    40.  
    41.         //send back the ID of the car to the client
    42.         OnSpawnedNetworkObjectClientRpc(spawned.NetworkObjectId);
    43.     }
    44.  
    45.     [ClientRpc]
    46.     public void OnSpawnedNetworkObjectClientRpc(ulong spawnedObjectID, ClientRpcParams rpcParams = default)
    47.     {
    48.         if (IsClient && m_SpawnedNetworkObject == null)
    49.         {
    50.             //find the spawned object
    51.             m_SpawnedNetworkObject = GetNetworkObject(spawnedObjectID);
    52.         }
    53.     }
    54. }
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,507
    I am not following exactly but the idea of spawning an object on both server and client sounds wrong. You are supposed to spawn on the server only, and the server will have the clients replicate the spawned object. Of course a client can trigger a spawn by calling a ServerRPC but the client itself cannot spawn network objects.

    One thought: try reversing setting the parent and assigning ownerships. Also, something tells me that you cannot have diverging ownership in a network object tree. So if the parent is host owned the children may not be set to be owned by clients. Respectively ownership may have to be set on the root network object. Please check if there is any mention of that in the docs.
     
  3. MafiaMoe

    MafiaMoe

    Joined:
    Mar 24, 2013
    Posts:
    27
    For the first point, everything is being spawned on the server side via ServerRpc, then the client finds the spawned object using the NetworkObjectId.

    For the second point, that is a good thought. I'll dig through the docs to see if there is any mention of parenting and ownership limitations.

    Thanks!
     
  4. MafiaMoe

    MafiaMoe

    Joined:
    Mar 24, 2013
    Posts:
    27
    After thinking it through, this seems like a bug. The server is instantiating and syncing everything and retains ownership of the NetworkObject that is having issues soft synching on clients. From what it sounds like, the netcode is mistaking the hosts NetworkObject as an 'In-Scene placed NetworkObject', when in fact it was instantiated. The error code backs up this theory exactly:

    [Netcode] NetworkPrefab hash was not found! In-Scene placed NetworkObject soft synchronization failure for Hash: 257822384!

    The Hash here is referring to the instantiated prefab, not an object pre-existing in the scene. I'm going to do some additional testing for a workaround, now that I have a better idea of what the problem is.
     
  5. MafiaMoe

    MafiaMoe

    Joined:
    Mar 24, 2013
    Posts:
    27
    And bingo. Waiting to spawn anything on the host/server does the trick. Here is the new code:

    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Netcode;
    3. using System.Collections;
    4.  
    5. public class CarSpawner : NetworkBehaviour
    6. {
    7.     [SerializeField] private GameObject m_CarPrefab;
    8.     [SerializeField] private NetworkObject m_SpawnedNetworkObject;
    9.     public NetworkObject LocalCarObject => m_SpawnedNetworkObject;
    10.  
    11.     public override void OnNetworkSpawn()
    12.     {
    13.         //tell the server to spawn a car for us
    14.         //SpawnClientCarServerRpc(NetworkManager.Singleton.LocalClientId); //this causes a soft-sync error on the host
    15.         StartCoroutine(WaitSpawn()); //wait until the server has had a chance to 'register' all the in-scene placed network objects before spawning anything
    16.     }
    17.  
    18.     private IEnumerator WaitSpawn()
    19.     {
    20.         yield return new WaitForSeconds(1);
    21.         SpawnClientCarServerRpc(NetworkManager.Singleton.LocalClientId);
    22.     }
    23.  
    24.     [ServerRpc(RequireOwnership = false)]
    25.     public void SpawnClientCarServerRpc(ulong clientID, ServerRpcParams rpcParams = default)
    26.     {
    27.         //instantiate the game object
    28.         GameObject instance = Instantiate(m_CarPrefab);
    29.  
    30.         //get the instances NetworkObject and Spawn
    31.         NetworkObject spawned = instance.GetComponent<NetworkObject>();
    32.         spawned.SpawnWithOwnership(clientID);
    33.         spawned.TrySetParent(gameObject);
    34.  
    35.         //send back the ID of the car to the client
    36.         OnSpawnedNetworkObjectClientRpc(spawned.NetworkObjectId);
    37.     }
    38.  
    39.     [ClientRpc]
    40.     public void OnSpawnedNetworkObjectClientRpc(ulong spawnedObjectID, ClientRpcParams rpcParams = default)
    41.     {
    42.         if (IsClient && m_SpawnedNetworkObject == null)
    43.         {
    44.             //find the spawned object
    45.             m_SpawnedNetworkObject = GetNetworkObject(spawnedObjectID);
    46.         }
    47.     }
    48. }

    I can guess all day as to why this is, but the Unity engineers would definitely have a better understanding than I.

    However, my best guess is that when a scene is loaded on the host/server, the check for all 'In-Scene placed NetworkObjects' is happening after the OnNetworkSpawn() is being called. In this case, it causes anything that gets spawned using OnNetworkSpawn() on the host/server to be mistaken for an 'In-Scene placed NetworkObject'.

    Then, when a client connects, an attempt it made to soft sync the host/server NetworkObjects as 'In-Scene placed' instead of instantiated, hence the error. Meanwhile, any objects spawned for clients happened well after the scene was loaded on the host/server, so those NetworkObjects get synced correctly as instantiated prefabs.

    The workaround is to have the host/server wait to spawn anything gives it a chance to check the scene for NetworkObjects that are indeed 'In-Scene placed'. Those instantiated NetworkObjects are then no longer mistaken as placed ones, meaning they soft sync correctly on the clients.
     
    Last edited: Jan 30, 2023
    slims likes this.