New client name only visible to host

Hey! When a player joins, my goal is to change the instantiated object name to the player id for all clients. Currently, the desired client name is only visible for the host, not for the player. (Appears as OBJECT(Clone)). I’m new to programming for multiplayer using Netcode, and I couldn’t much in the documentation.

(The print “it should sync the names…” is executed every time a client joins on the host side, I would like it to execute on all clients including host, in order to sync the names)

    private void OnClientConnected(ulong clientId)
    {
        if (NetworkManager.Singleton.IsServer)
        {
            // Instantiate the first prefab and assign ownership to the connected client
            GameObject instance1 = Instantiate(prefab1, Vector2.zero, Quaternion.identity);
            NetworkObject networkObject1 = instance1.GetComponent<NetworkObject>();
            networkObject1.SpawnAsPlayerObject(clientId);


          

          

            // Instantiate the second prefab and assign ownership to the connected client
            GameObject instance2 = Instantiate(prefab2, Vector2.zero, Quaternion.identity);
            NetworkObject networkObject2 = instance2.GetComponent<NetworkObject>();
            networkObject2.SpawnAsPlayerObject(clientId);

          

            SetObjectNamesRpc(instance1, instance2, clientId);

            GunHandler gunHandler = instance2.GetComponent<GunHandler>();
            gunHandler.UpdateDataClientRpc(instance1.transform.name);
          


        }
    }

    [ClientRpc]
    private void SetObjectNamesRpc(GameObject fishObject, GameObject gunObject, ulong clientId)
    {
        print("it should sync the names...");
        fishObject.transform.name = "Fish " + clientId.ToString();
        gunObject.transform.name = "Gun " + clientId.ToString();
    }

The whole class

using UnityEngine;
using Unity.Netcode;

public class PlayerJoinHandler : MonoBehaviour
{
    [SerializeField] private GameObject prefab1; // Assign your first prefab in the inspector
    [SerializeField] private GameObject prefab2; // Assign your second prefab in the inspector

    private void Start()
    {
        NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
    }

    private void OnDestroy()
    {
        if (NetworkManager.Singleton != null)
        {
            NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected;
        }
    }

    private void OnClientConnected(ulong clientId)
    {
        if (NetworkManager.Singleton.IsServer)
        {
            // Instantiate the first prefab and assign ownership to the connected client
            GameObject instance1 = Instantiate(prefab1, Vector2.zero, Quaternion.identity);
            NetworkObject networkObject1 = instance1.GetComponent<NetworkObject>();
            networkObject1.SpawnAsPlayerObject(clientId);


           

           

            // Instantiate the second prefab and assign ownership to the connected client
            GameObject instance2 = Instantiate(prefab2, Vector2.zero, Quaternion.identity);
            NetworkObject networkObject2 = instance2.GetComponent<NetworkObject>();
            networkObject2.SpawnAsPlayerObject(clientId);

           

            SetObjectNamesRpc(instance1, instance2, clientId);

            GunHandler gunHandler = instance2.GetComponent<GunHandler>();
            gunHandler.UpdateDataClientRpc(instance1.transform.name);
           


        }
    }

    [ClientRpc]
    private void SetObjectNamesRpc(GameObject fishObject, GameObject gunObject, ulong clientId)
    {
        print("it should sync the names...");
        fishObject.transform.name = "Fish " + clientId.ToString();
        gunObject.transform.name = "Gun " + clientId.ToString();
    }


    //private Vector3 GetRandomSpawnPosition()
    //{
    //    // Replace this with your spawn logic
    //    return new Vector3(Random.Range(-10, 10), 0, Random.Range(-10, 10));
    //}
}

You should actually get an error for this because a GameObject cannot be sent as a RPC parameter.
Instead make these NetworkObject because NetworkObject components can be sent over the network - effectively only the NetworkObjectId gets sent (a ulong = 8 bytes).

You will have to add a string parameter with the name to the RPC method too.

Can be written more concise as:

        fishObject.name = $"Fish {clientId}";
        gunObject.name = $"Gun {clientId}";

Turns out the ClientRpc has to be inside a network object, does this also mean that ServerRpcs have to be inside network managers? Thanks, didn’t know how to write formatted strings in C# until now :slight_smile:

To be precise, RPCs function only in NetworkBehaviour classes which require a NetworkObject component either on the same or a parent object.

This goes for both Client and Server RPCs.

Before a component gets too complex with many RPCs I tend to make sub-components that handles one specific aspect only, such as reloading a weapon - there’s a NetworkWeaponReload component, it hs the RPCs to let the server know of the reload and confirm it to the client, and contains the reload functionality ie driving the animation and resetting ammo count and such.

Because that’s easier to test and modify than a single NetworkWeapon component that also does the shooting, weapon switching and hit detection (which really isn’t the weapon’s job) and what not.