Search Unity

Question Clients don't get updates

Discussion in 'Netcode for GameObjects' started by XeLLLeR, Apr 25, 2022.

  1. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Hey there! I bumped into strange problem. When I'm in the right scene and I'm a host I start spawning players and set names and color for them. From the host view everything looks good, but from the client I only see spawned players(right amount) without any color and name. I tried a lot of variants to spawn and setup (SpawnAsPlayerObject, SpawnWithOwnerShip, Call clientRpc and not call, inherit from NetworkBehaviour and not inherit), but still have the same result. The way how I do it now:
    Code (CSharp):
    1.     public class SpawnPlayers : MonoBehaviour
    2.     {
    3.         [SerializeField] private GameObject playerPrefab;
    4.        
    5.         private void OnEnable()
    6.         {
    7.             if (!NetworkManager.Singleton.IsHost) return;
    8.  
    9.             foreach (var player in NetworkManager.Singleton.ConnectedClientsList)
    10.             {
    11.                 SpawnPlayerServerRpc(player.ClientId);
    12.             }
    13.         }
    14.        
    15.         [ServerRpc(RequireOwnership = false)]
    16.         private void SpawnPlayerServerRpc(ulong userID)
    17.         {
    18.             GameObject player = Instantiate(playerPrefab, new Vector3(Random.Range(-10f, 10f), 1f, 0f),
    19.                 Quaternion.identity);
    20.             player.GetComponent<NetworkObject>().SpawnWithOwnership(userID, true);
    21.             string username = UserConfig.Instance.GetNameByID(userID);
    22.             player.GetComponent<PlayerEntity>().Name = username;
    23.             player.GetComponentInChildren<MeshRenderer>().material.color = Color.black;
    24.         }
    25.     }
     
  2. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    The player is spawned, and then only on the server's copy the name and color are being set. These are not properties which are automatically synched across clients. I'd advise adding a ClientRpc that does this for the clients as well, that you call from SpawnPlayerServerRpc, passing in the username and a NetworkObjectReference to the newly spawned player.
     
  3. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Yeah, I've tried this. But it didn't work too. I tried to pass id and find network object by this id. Now I pass NetworkObject as you said and it still doesn't work. Current variant:
    Code (CSharp):
    1.     public class SpawnPlayers : MonoBehaviour
    2.     {
    3.         [SerializeField] private GameObject playerPrefab;
    4.        
    5.         private void OnEnable()
    6.         {
    7.             if (!NetworkManager.Singleton.IsHost) return;
    8.  
    9.             foreach (var player in NetworkManager.Singleton.ConnectedClientsList)
    10.             {
    11.                 SpawnPlayerServerRpc(player.ClientId);
    12.             }
    13.         }
    14.        
    15.         [ServerRpc(RequireOwnership = false)]
    16.         private void SpawnPlayerServerRpc(ulong userID)
    17.         {
    18.             GameObject player = Instantiate(playerPrefab, new Vector3(Random.Range(-10f, 10f), 1f, 0f),
    19.                 Quaternion.identity);
    20.             player.GetComponent<NetworkObject>().SpawnWithOwnership(userID, true);
    21.             string username = UserConfig.Instance.GetNameByID(userID);
    22.             SetNameToPlayerClientRpc(player.GetComponent<NetworkObject>(), username);
    23.         }
    24.  
    25.         [ClientRpc]
    26.         private void SetNameToPlayerClientRpc(NetworkObject player, string username)
    27.         {
    28.             player.GetComponent<PlayerEntity>().Name = username;
    29.             player.GetComponentInChildren<MeshRenderer>().material.color = Color.black;
    30.         }
    31.     }
     
  4. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    private void SetNameToPlayerClientRpc(NetworkObject player, string username)
    should be
    private void SetNameToPlayerClientRpc(NetworkObjectReference player, string username)
    . Also, does this still work on the server, but not on other clients? Did you try printing the NetworkObjectId each client receives in
    SetNameToPlayerClientRpc
    to see if something's going awry there?
     
  5. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    Console shows two different objects id. Tried to do with NetworkObjectReference and SpawnManager, still have the same problem
    Code (CSharp):
    1.         [ServerRpc(RequireOwnership = false)]
    2.         private void SpawnPlayerServerRpc(ulong userID)
    3.         {
    4.             GameObject player = Instantiate(playerPrefab, new Vector3(Random.Range(-10f, 10f), 1f, 0f),
    5.                 Quaternion.identity);
    6.             player.GetComponent<NetworkObject>().SpawnWithOwnership(userID, true);
    7.             string username = UserConfig.Instance.GetNameByID(userID);
    8.             SetNameToPlayerClientRpc(player.GetComponent<NetworkObject>(), username);
    9.         }
    10.  
    11.         [ClientRpc]
    12.         private void SetNameToPlayerClientRpc(NetworkObjectReference player, string username)
    13.         {
    14.             print(player.NetworkObjectId);
    15.             NetworkManager.Singleton.SpawnManager.SpawnedObjects[player.NetworkObjectId].
    16.                 GetComponent<PlayerEntity>().Name = username;
    17.             NetworkManager.Singleton.SpawnManager.SpawnedObjects[player.NetworkObjectId].
    18.                 GetComponentInChildren<MeshRenderer>().material.color = Color.black;
    19.         }
     
  6. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    What may be happening is that the function on the non-server client is called too early, i.e. before the player is properly spawned. The way I've set something similar up myself is to have the player script (so on the player object) call a function like RequestDataServerRpc from its OnNetworkSpawn, which then has the server call a ClientRpc that sets the player data. A bit cumbersome, but that way you can be sure that the function is not being called before the object is properly spawned.
     
  7. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    The same result :(
    Code (CSharp):
    1.     public class PlayerEntity : NetworkBehaviour
    2.     {
    3.         public delegate void OnSpawn(ulong userID, ulong objectID);
    4.         public static event OnSpawn OnSpawnEvent;
    5.        
    6.         public string Name
    7.         {
    8.             get => _name;
    9.             set
    10.             {
    11.                 _name = value;
    12.                 _nameplate.text = value;
    13.             }
    14.         }
    15.         private string _name;
    16.  
    17.         private TextMeshPro _nameplate;
    18.  
    19.         public override void OnNetworkSpawn()
    20.         {
    21.             base.OnNetworkSpawn();
    22.  
    23.             _nameplate = GetComponentInChildren<TextMeshPro>();
    24.            
    25.             OnSpawnEvent?.Invoke(OwnerClientId, NetworkObjectId);
    26.         }
    27.     }
    Code (CSharp):
    1.     public class SpawnPlayers : MonoBehaviour
    2.     {
    3.         [SerializeField] private GameObject playerPrefab;
    4.        
    5.         private void OnEnable()
    6.         {
    7.             if (!NetworkManager.Singleton.IsHost) return;
    8.  
    9.             PlayerEntity.OnSpawnEvent += SetNameToPlayerServerRpc;
    10.  
    11.             foreach (var player in NetworkManager.Singleton.ConnectedClientsList)
    12.             {
    13.                 SpawnPlayerServerRpc(player.ClientId);
    14.             }
    15.         }
    16.        
    17.         [ServerRpc(RequireOwnership = false)]
    18.         private void SpawnPlayerServerRpc(ulong userID)
    19.         {
    20.             GameObject player = Instantiate(playerPrefab, new Vector3(Random.Range(-10f, 10f), 1f, 0f),
    21.                 Quaternion.identity);
    22.             player.GetComponent<NetworkObject>().SpawnWithOwnership(userID, true);
    23.         }
    24.  
    25.         [ServerRpc(RequireOwnership = false)]
    26.         private void SetNameToPlayerServerRpc(ulong userID, ulong objectID)
    27.         {
    28.             string username = UserConfig.Instance.GetNameByID(userID);
    29.             SetNameToPlayerClientRpc(objectID, username);
    30.         }
    31.  
    32.         [ClientRpc]
    33.         private void SetNameToPlayerClientRpc(ulong objectID, string username)
    34.         {
    35.             NetworkManager.Singleton.SpawnManager.SpawnedObjects[objectID].
    36.                 GetComponent<PlayerEntity>().Name = username;
    37.             NetworkManager.Singleton.SpawnManager.SpawnedObjects[objectID].
    38.                 GetComponentInChildren<MeshRenderer>().material.color = Color.black;
    39.         }
    40.     }
     
  8. XeLLLeR

    XeLLLeR

    Joined:
    Dec 5, 2020
    Posts:
    30
    I've finally fixed it! I added NetworkBehaviour to SpawnPlayers script and added NetworkObject to it's object in unity. After this I've changed OnEnable method to the OnNetworkSpawn. So this is my final variant that works:
    Code (CSharp):
    1.     public class SpawnPlayers : NetworkBehaviour
    2.     {
    3.         [SerializeField] private GameObject playerPrefab;
    4.  
    5.         public override void OnNetworkSpawn()
    6.         {
    7.             base.OnNetworkSpawn();
    8.             if (!NetworkManager.Singleton.IsHost) return;
    9.  
    10.             foreach (var player in NetworkManager.Singleton.ConnectedClientsList)
    11.             {
    12.                 SpawnPlayerServerRpc(player.ClientId);
    13.             }
    14.         }
    15.        
    16.         [ServerRpc(RequireOwnership = false)]
    17.         private void SpawnPlayerServerRpc(ulong userID)
    18.         {
    19.             GameObject player = Instantiate(playerPrefab, new Vector3(Random.Range(-10f, 10f), 1f, 0f),
    20.                 Quaternion.identity);
    21.             player.GetComponent<NetworkObject>().SpawnAsPlayerObject(userID, true);
    22.            
    23.             string username = UserConfig.Instance.GetNameByID(userID);
    24.             SetNameToPlayerClientRpc(player.GetComponent<NetworkObject>().NetworkObjectId, username);
    25.         }
    26.  
    27.         [ClientRpc]
    28.         private void SetNameToPlayerClientRpc(ulong objectID, string username)
    29.         {
    30.             NetworkManager.Singleton.SpawnManager.SpawnedObjects[objectID].GetComponent<PlayerEntity>().Name = username;
    31.         }
    32.     }
     
  9. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Ah, I should've noticed that that script was a MonoBehaviour! Weird that it didn't give an error given that you use Rpcs and the like, but glad it's fixed.