Search Unity

Help Understanding ClientRpc function

Discussion in 'Multiplayer' started by AnthonyGDI, Mar 21, 2017.

  1. AnthonyGDI

    AnthonyGDI

    Joined:
    Feb 5, 2016
    Posts:
    20
    Hello everyone,

    I put a ton of debug messages into some code on a local player object to try to figure out what was going on but I am still not sure what is happening.

    Two players connect, so I expect the Start function to trigger twice on the server, one for each player. This part does happen. Immediately below that is the call to the ClientRpc function which I thought would be called on each of the clients per function call. The main issue is that RpcSetupPlayer is only being called one time on one client.
    Code (CSharp):
    1.  
    2. void Start () {
    3.         if (!isServer)
    4.         {
    5.             print(" S!S: " + netId.Value.ToString());
    6.             return;
    7.         }
    8.  
    9.         print(" SS: " + netId.Value.ToString());
    10.  
    11.         if (isLocalPlayer)
    12.         {
    13.             print(" Local: " + netId.Value.ToString());
    14.         }
    15.  
    16.         RpcSetupPlayer(GameManagerScript.current.GetPlayerPiece());
    17. }
    18.  
    19. [ClientRpc]
    20. void RpcSetupPlayer(char c_piece)
    21. {
    22.     print("Rpc: " + c_piece + " " + netId.Value.ToString());
    23.     if(isLocalPlayer)
    24.     {
    25.         piece = c_piece;
    26.         GameManagerScript.current.boardGO.GetComponent<BoardClickHandler>().clientPiece = piece;
    27.     }
    28. }
    29.  
    Thanks for looking.
     
  2. donnysobonny

    donnysobonny

    Joined:
    Jan 24, 2013
    Posts:
    220
    Hmm I might need to see a bit more of your code, but ultimately it depends where you initiate the RpcSetupPlayer method. In unet, a ClientRPC is designed to be called on the server, on an object that is networked. For example, if you were to spawn an object over the network, and within a NetworkBehaviour script attached to that object you were to call a ClientRPC on the server-side, that RPC would be called on the instance of that object in each client.

    Where does the above code exist? Does it exist within a NetworkBehaviour script attached to a network spawned object?
     
  3. AnthonyGDI

    AnthonyGDI

    Joined:
    Feb 5, 2016
    Posts:
    20
    Yes, it does exist within a NetworkBehaviour script attached to a network spawned object. It is attached to the Player Prefab.

    Does a ClientRPC also get called on the Host?
     
  4. donnysobonny

    donnysobonny

    Joined:
    Jan 24, 2013
    Posts:
    220
    Okay in that case your issue is because you are initiating the ClientRPC from the client-side, based on the Start method of your code. You should be using a Command here, as Commands are designed to be initiated by the client, on networked objects. ClientRPCs, as mentioned in my previous post, should be initiated from the server-side, and will result in the RPC being called on the same object in all clients.

    Also, I would replace your Start method here with OnStartClient or something similar, since the Start method might be called before the object is actually ready over the network.

    In the case that you are using a peer-to-peer model, (where one of your players is also the server), then yes. Because that person is both a server AND a client, so they will receive the ClientRPC on their client-spawned object.
     
  5. AnthonyGDI

    AnthonyGDI

    Joined:
    Feb 5, 2016
    Posts:
    20
    I changed my code to reflect my current understanding. It seems like the function RpcSetupMyPiece is only being called twice, which would make me believe it is not being called on the server instance of the objects? One of the clients is functioning as a host, so this seems to go against what you just explained. This is the main part I am confused with at the moment. I thought RpcCalls would be activated on the host's instance of the object, and all other client's instance of that object but it seems to not be doing this. I am using CmdSetupMyPiece to set the server/host instance's variable, but if RpcSetupMyPiece is called on all clients (including the host's client) then I wouldn't have to set the variable in CmdSetupMyPiece since it would be set in RpcSetupMyPiece immediately afterwards.

    Code (CSharp):
    1.  
    2. public class PlayerScript : NetworkBehaviour
    3. {
    4.     public char myPiece;
    5.  
    6.     public override void OnStartLocalPlayer()
    7.     {
    8.         print("OnStartLocalPlayer netId:" + netId.Value.ToString());
    9.         CmdSetupMyPiece();
    10.     }
    11.  
    12.     [Command]
    13.     void CmdSetupMyPiece()
    14.     {
    15.         print("Is this called once per object on the host? netId:" + netId.Value.ToString());
    16.  
    17.         myPiece = 'x'; //Is this step unnecessary?
    18.  
    19.         RpcSetupMyPiece('x');
    20.     }
    21.  
    22.     [ClientRpc]
    23.     void RpcSetupMyPiece(char passedChar)
    24.     {
    25.         print("Is this called once per object on clients? or 4 times? netId:" + netId.Value.ToString());
    26.         myPiece = passedChar;
    27.     }
    28. }
    29.  
    Actually I just realized that the first player does not get a single call from RpcSetupMyPiece, but the second one gets two calls, one for the host's instance and one for the other client's instance. Here is my Debug.Log

    OnStartLocalPlayer netId:3

    Is this called once per object on the host? netId:3

    OnStartLocalPlayer netId:4

    Is this called once per object on the host? netId:4

    Is this called once per object on clients? or 4 times? netId:4

    Is this called once per object on clients? or 4 times? netId:4
     
  6. AnthonyGDI

    AnthonyGDI

    Joined:
    Feb 5, 2016
    Posts:
    20
    I guess this would all make sense if the server can't send itself Commands!

    Sorry for all of the posts but I keep thinking that I've figured it out and then it fails some new seemingly related way that I don't understand. After figuring that the server/host can't send itself commands I changed my code a tiny bit
    Code (CSharp):
    1.  
    2. public class PlayerScript : NetworkBehaviour
    3. {
    4.     public char myPiece;
    5.  
    6.     public override void OnStartLocalPlayer()
    7.     {
    8.         print("OnStartLocalPlayer netId:" + netId.Value.ToString());
    9.         if (!isServer)
    10.         {
    11.             CmdSetupMyPiece();
    12.         }
    13.         else
    14.         {
    15.             RpcSetupMyPiece('x');
    16.         }
    17.     }
    18.  
    19.     [Command]
    20.     void CmdSetupMyPiece() //Is this step unneccessary?
    21.     {
    22.         print("Is this called once per object on the host? netId:" + netId.Value.ToString());
    23.  
    24.         RpcSetupMyPiece('x');
    25.     }
    26.  
    27.     [ClientRpc]
    28.     void RpcSetupMyPiece(char passedChar)
    29.     {
    30.         print("Is this called once per object on clients? or 4 times? netId:" + netId.Value.ToString());
    31.         myPiece = passedChar;
    32.     }
    33.  
    What does "Did not find target for RPC message for 4" mean, and how do I make it behave how I want it to behave?

    New Debug Log:
    OnStartLocalPlayer netId:3
    OnStartLocalPlayer netId:5
    Is this called once per object on the host? netId:5
    Is this called once per object on clients? or 4 times? netId:5
    Is this called once per object on clients? or 4 times? netId:5
    Did not find target for RPC message for 4
    Did not find target for RPC message for 4
     
    Last edited: Mar 21, 2017
  7. donnysobonny

    donnysobonny

    Joined:
    Jan 24, 2013
    Posts:
    220
    I think you may be getting confused. You may also not be, and I may be misunderstanding exactly what it is that you're having issues with here, to lets take a step back and look at what we know to be the case (based on documentation provided by unity).

    A short disclaimer first: my responses here are based on the documentation. I don't use RPCs and Commands due to the bloated overhead they cause in resources. So hopefully the documentation is not wrong!
    1. It sounds like you are using the peer-hosted setup, where one of your players acts as a player AND the server. If this is not the case, and you are creating a dedicated server-side (where your server-side will be run on a dedicated server, and not handled by a player), please say, as otherwise this will cause confusion
    2. in a peer-hosted/peer-to-peer setup, one of your players is both a player AND a server. This means that when I talk about a "client" and when I talk about a "server", BOTH apply to the host-player, while only the "client" part applies to everyone else
    3. based on unity's documentation, a ClientRPC is something that is called on the server-side, on a networked object. So in the code that is run by your server, you would invoke the RPC method. This would result in the RPC method being called on ALL clients (including the peer-host if the peer-host is also a client!) for the networked object
    4. again based on unity's documentation, a Command is something that you invoke on the client-side, on a networked object. So in the code that is run by a client, you would invoke the Command method. This would result in the Command method being called on the server-side version of the object.
    5. The above two points require that the "networked" object has a script that inherits NetworkBehaviour, and your RPCs and Commands are invoked and defined within this script
    6. The above object that contains the script should also be a registered prefab (registered via ClientScene.RegisterPrefab, which is handled for you automatically if you are setting the prefab as the player prefab via the NetworkManager)
    7. The above prefab should also contain a NetworkIdentity component
    8. The above prefab should first be instantiated on the server side, before the server called NetworkServer.Spawn (which happens automatically if you use a player prefab assigned to the NetworkManager component)
    The majority of the information above comes from here: https://docs.unity3d.com/Manual/UNetActions.html. By the sound of things, either you are using RPCs and Commands incorrectly, or the documentation is wrong. So I advise that if you're still struggling, read carefully through the above document and ensure that you are doing things correctly.

    Hopefully this helps. Good luck!
     
  8. AnthonyGDI

    AnthonyGDI

    Joined:
    Feb 5, 2016
    Posts:
    20
    I am using a peer-hosted setup.

    I read the UNetActions page as well and had the opinions about how ClientRPC and Command should work.

    I think that my main issue was that I sometimes calling the ClientRPC function before some clients and objects were setup. So using ClientRPC function inside of Start, or OnLocalPlayerStart functions is not a great idea. I got my game to work by creating a boolean to check if all of the clients were ready before initializing the players. The update function checks to see if the players are all ready to be setup before setting them up.

    Thanks for your help and opinions.
     
  9. ferrisbuellers

    ferrisbuellers

    Joined:
    Mar 28, 2017
    Posts:
    2
    hey man, how did you odo the boolean check