ClientRPC not called from command on client instance

Good morning all,

I am implementing a Server/Client based multiplayer game using Commands and ClientRPC’s.

  1. I currently have a GameController (server only) object that contains information about the game.
  2. Upon a new client joining the game, I am attempting to initialise the player with information contained in the GameController (i.e. TeamId, SpawnLocation).
  3. In the Start() method, I call CmdInitialisePlayer(), which requests data from the GameController and calls ClientRPC’s to save that data on the local client Player object.
public class Player : NetworkBehaviour
{
    private int id;
    private int myTeamId;
   
    public const string PLAYER_TAG = "Player";

    void Start()
    {
        id = FindObjectsOfType<Player>().Length - 1;
        RegisterModel(PLAYER_TAG, id);
        spawnController = GameObject.FindGameObjectWithTag(SpawnController.SPAWN_CONTROLLER_TAG).GetComponent<SpawnController>();

               
        if (isLocalPlayer)
        {
            // Player Initialisation
            CmdInitialisePlayer();
                      
            // Canvas Settings
            Canvas canvas = GetComponentInChildren<Canvas>();
            canvas.planeDistance = 1;
            canvasController = canvas.GetComponent<CanvasController>();
        }

        if (!isLocalPlayer)
        {
            DisableNonLocalCompontents();
            AssignLayer(REMOTE_LAYER_NAME);
        }
    }

    [Command]
    void CmdInitialisePlayer()
    {
        GameController gameController = GameObject.FindGameObjectWithTag(GameController.GAME_CONTROLLER_TAG).GetComponent<GameController>();
        Transform transform = gameController.GetPlayerTransform(id);

        RpcSetPlayerTransform(transform.position, transform.rotation);
        RpcSetMyTeamId(gameController.GetMyTeamControllerId(id));
    }

    [ClientRpc]
    void RpcSetPlayerTransform(Vector3 position, Quaternion rotation)
    {
        Debug.Log("rpc transform");
        transform.rotation = rotation;
        transform.position = position;
    }


    [ClientRpc]
    void RpcSetMyTeamId(int teamId)
    {
        Debug.Log("rpc myTeamId");
        myTeamId = teamId;
    }
}

I believe this is the correct way to have my code structured and when I run it within a host/client setup everything works as expected. However, when a client only instance joins, the ClientRPC’s appear to not be executed as my Debug.Log() calls are not displayed within the logs, furthermore, the Player.myTeamId or Player.transform parameters are not set.

Any help with this would be greatly appreciated.

2 Likes

The ClientRpc’s will not be called on existing Player GameObjects already in the game for new clients that join. That is because you’re calling them in a Command that is only executed when called by the localplayer, and ClientRpc’s are not cached. So the ClientRpc’s will only execute on all clients already connected to the server when the rpc is called.

You could resolve this in one of several ways. You could call the ClientRpc’s again on all existing Player objects every time a new client connects, but this would run them additional times unnecessarily on all existing clients. You could change to using TargetRpc’s and call them on all existing Player GameObjects directing them at the newly connecting client.

The most simple way though would be to get rid of the ClientRpc’s and just make everything you’re sending SyncVars. SyncVars are synced with their current values to all newly connected clients automatically. This makes them far more useful for setting any initialization settings needed when the object is spawned on newly connecting clients than are ClientRpc’s.

How are you spawning your player object? I’ve seen issues like yours if I accidentally put one of these objects in a scene or Instiantiate() it. Remember that you should use NetworkServer.Spawn in order to spawn new objects that will sync. If you use anything else, you will have a copy on your server and one on your client, but they will not share a network id.

I would also recommend having a manager for your players, that way when you have a command that you want to propogate to specific (or all) players, you can loop through them. I allow all of my players to be network objects that each have a local player manager. I manage an array of all players and when I want to do specific things on specific players, I send the index of the player through my Cmd, which will then select the correct player and send the corresponding Rpc.