Search Unity

[Unet] How do you know that clients are ready to act on ClientRpc calls?

Discussion in 'UNet' started by Edvard-D, Apr 17, 2017.

  1. Edvard-D

    Edvard-D

    Joined:
    Jun 14, 2012
    Posts:
    129
    I'm working on a game initialization manager, and am using a custom lobby manager to initiate the process. I overrode "OnLobbyServerPlayersReady" so that instead of loading the scene, it starts the initiation process. Part of that is, of course, loading the scene, but it also includes turning on/off the loading screen, random terrain gen, and moving the players to the start spawn points. The system syncs the process across all clients, so it waits for all clients to load their scenes before it tells them to generate the terrain. Once they've all generated their terrain, it tells them to move the players to the start spawn points.

    So, after the scene loading initialization step is done, the GenerateBaseTerrain step gets initiated. The problem is that a ClientRpc to generate the terrain is called by the server, but the message isn't received by the client. This is even the case in single player (still built off of the networking system and uses the same initialization process). The GenerateBaseTerrain step gets called by the NetworkLobbyManager OnLobbyServerSceneLoadedForPlayer method; I've also tried calling it in the NetworkLobbyPlayer OnClientExitLobby method, but it's the same result.

    My thinking is that the client isn't listening to/ready for the ClientRpc call when it gets activated, but I'm not sure what I need to check to make sure they are. During the first step of the initialization process--before the scene reload--another ClientRpc method is called to have the loading UIs turned on. This is working perfectly fine, so I know I have all the pieces in place for it to work.

    This is the ClientRpc method I'm trying to call, just in case I'm doing some there that I'm not supposed to. Note that "RpcInstantiateBaseTerrain" is never getting called in the code below, which lets me know that the method isn't getting called at all.
    Code (CSharp):
    1.     [ClientRpc]
    2.     public void RpcInstantiateBaseTerrain(BaseTerrainTypes baseTerrainType)
    3.     {
    4.         Debug.Log("RpcInstantiateBaseTerrain");
    5.         switch (baseTerrainType)
    6.         {
    7.             case BaseTerrainTypes.Default:
    8.                 GameObject.Instantiate(DefaultBaseTerrainPrefab);
    9.                 break;
    10.  
    11.             default:
    12.                 Debug.LogError("No case for BaseTerrainType " + baseTerrainType.ToString() + " exists.");
    13.                 break;
    14.         }
    15.     }
     
  2. Schwartz_M

    Schwartz_M

    Joined:
    May 7, 2018
    Posts:
    2
    Did you ever figure this out? I'm struggling with a very similar problem. I've proved that the issue is the timing of the ClientRpc -- that is, the clients aren't ready to receive messages yet. But it's important to know at what point in the game flow the clients are ready, and how to access that point.
     
  3. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    UNET processes incoming messages while scene loads are in progress, which could be the reason for your problem.
     
  4. Schwartz_M

    Schwartz_M

    Joined:
    May 7, 2018
    Posts:
    2
    Hey vis2k, I don't think that's the problem, as this code runs after the scene loads. Take a look: (In this script I am trying to instantiate a playing card on the server and spawn it on all clients. This works. Then I change the sprite associated with the card, and try to update it on the client with an ClientRpc. This does not run. Why?)

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Networking;
    5.  
    6. public class GameController : NetworkBehaviour {
    7.  
    8.     public GameObject card;
    9.     public Sprite[] faces;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.  
    14.         if (!isServer) {
    15.             return;
    16.         }
    17.      
    18.         // Instantiate a card on the server, change its sprite, and then spawn to the clients    
    19.         GameObject cardInst = Instantiate(card);
    20.         SpriteRenderer spriteRenderer = cardInst.GetComponentInChildren<SpriteRenderer>();
    21.         spriteRenderer.sprite = faces[1];    
    22.         NetworkServer.Spawn(cardInst);
    23.         RpcUpdateSprite(cardInst);
    24.  
    25.  
    26.     }
    27.  
    28.     [ClientRpc]
    29.     void RpcUpdateSprite(GameObject CI) {
    30.  
    31.         SpriteRenderer CIspr = CI.GetComponentInChildren<SpriteRenderer>();
    32.         CIspr.sprite = faces[1];
    33.         Debug.Log("HI");
    34.     }
    35. }
     
  5. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    default unet uses udp. maybe the rpc packet reaches the client before the spawn packet.
    either way you are probably better off adding a networkbehaviour to your card prefab and use on(de)serialize to set the sprite. if you use an rpc then new observers will still see the old sprite.
     
  6. djordjenikolic97

    djordjenikolic97

    Joined:
    Apr 5, 2019
    Posts:
    1
    I think this will fix your problem, your object will be ready to receive ClientRpc calls when OnStartClient() has been called.