Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Resolved ServerRpc won't run when called by client.

Discussion in 'Netcode for GameObjects' started by Calothamnus, May 1, 2024.

  1. Calothamnus

    Calothamnus

    Joined:
    Jul 22, 2023
    Posts:
    3
    Heya, I'm fairly new to multiplayer dev. I come here because I've scoured the forums and documentation myself and cannot find a solution. I've got a ServerRpc that just.. won't run. I've ensured it's being called on a spawned NetworkBehaviour, with a NetworkObject present. Their NetworkObjectIds are consistent both on host and client, and the code runs just fine when called by the host. The behaviour I'm seeing is consistent with hosting from both the build and in-editor, and I'm quite frankly stumpted at this point.

    I'm using Lobby and Relay to join clients via a secure p2p connection.

    After a host is started, it calls this ServerRpc. It runs as intended, and adds the player's details to a list on the object.
    Code (CSharp):
    1. [ServerRpc(RequireOwnership = false)]
    2.     public void OnPlayerJoinedServerRpc(string addition)
    3.     {
    4.         Debug.Log("OPJSRPC" + addition);
    5.         lobby.connectedPlayers.Add(JsonConvert.DeserializeObject<ConnectedPlayer>(addition));
    6.         string playerList = JsonConvert.SerializeObject(lobby.connectedPlayers);
    7.         UpdatePlayerListClientRpc(playerList);
    8.     }
    Everything works fine, and the player's name appears in my lobby's connectedPlayers display. When a client is started, it starts this coroutine (to ensure the client has fully initialized before continuing - the same behaviour is seen when directly running the ServerRpc.
    Code (CSharp):
    1. IEnumerator OnPlayerJoinedWait()
    2.     {
    3.         yield return new WaitUntil(() => NetworkManager.LocalClient != null);
    4.         Debug.Log("Client Online, running OPJSRPC");
    5.         string json = JsonConvert.SerializeObject(new ConnectedPlayer()
    6.         {
    7.             clientID = NetworkManager.Singleton.LocalClientId,
    8.             playerID = AuthenticationService.Instance.PlayerId,
    9.             playerName = AuthenticationService.Instance.PlayerName,
    10.             team = PlayerTeam.None
    11.         }, Formatting.Indented);
    12.         Debug.Log("Converted ConnectedPlayer to json object:\n" + json);
    13.         OnPlayerJoinedServerRpc(json);
    14.         Debug.Log("Called OPJSRPC");
    15.     }
    On the host, I recieve the following debug logs:
    (start host)
    "OPJSRPC [playerjson]"
    "UPLCRPC" (a log confirming UpdatePlayerListClientRpc was called)
    (start client)
    (nothing)

    On the client, I recieve the following debug logs:
    (start client)
    "Client Online, running OPJSRPC"
    "Converted ConnectedPlayer to json object: [playerjson]"
    "Called OPJSRPC"

    The host does not recieve the rpc call, and the client is not given the returning ClientRpc. I have confirmed the client and host are correctly connected, as attempting to load a scene with
    NetworkManager.SceneManager.LoadScene("Gameplay", UnityEngine.SceneManagement.LoadSceneMode.Single);
    correctly loads the called scene. Any help would be greatly appreciated. I'm worried I'm going to have to do something hacky and put the RPC calls on a separate NetworkBehaviour on a prefab and spawn it when the host is created, but that sounds pretty jank.
     
  2. Calothamnus

    Calothamnus

    Joined:
    Jul 22, 2023
    Posts:
    3
    Nevermind, my client wasn't connected yet - I thought LocalClient would be null until a connection was successful, but that wasn't the case. I fixed this by replacing
    yield return new WaitUntil(() => NetworkManager.Singleton.LocalClient != null);
    with
    yield return new WaitUntil(() => NetworkManager.Singleton.IsConnectedClient);
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,942
    You're setting this up incorrectly. You don't wait for a client to be connected. Instead, you have events "client connected" or you could start the coroutine in a network object's OnNetworkSpawn method and thus know that the client is connected.

    I'm also skeptical about you synchronizing the connected players list. NetworkManager already knows about the connected clients and I believe this is available for all clients.

    With the exception of a player's name - which you can pass in during connection approval as part of the payload. The server stores that player name for the given client id in a dictionary. If the client gets approved, the server calls a ClientRpc "NewPlayerConnectedHeresTheName(ulong clientId, string playerName, byte teamId). Clients don't have to advertise to the server that they are connected - the server already knows! You only need to listen to client approval and connected callbacks.

    Refrain from sending Json formatted strings over the network, this unnecessarily bloats traffic for which you'll eventually will have to pay for! It may not sound like much but consider thousands of players causing 5 times as much traffic as necessary and you'll pay for 5 times the bandwidth (or hit the pay limit five times earlier). Assuming you are using services like relay or server hosting or continue to write such bandwidth-wasteful netcode.
     
  4. Calothamnus

    Calothamnus

    Joined:
    Jul 22, 2023
    Posts:
    3
    I appreciate the insight - this is a project for university which I'm unlikely to release fully using the current systems. Ideally, I'll end up using steamworks' lobby system instead which provides the information I need by default, for the most part. I'm only using unity's systems because of limitations with my uni's computers (namely, the inability to install steam in the first place).

    HOWEVER, your notes on how I'm handling the connections are still super valid and useful to me, and I'll take it into account when further developing this project. I'd also like to thank you for your contributions to the forums over the years, because I often see you, Kurt-Dekker and Antistone in threads that have solved various issues I've come across while learning.

    I'll keep your feedback in mind and implement some less wasteful code once I have time in my roadmap for now.
     
    CodeSmile likes this.