Home > Blockchain >  Unity Netcode ServerRpc doesn't get executed when called by Host itself
Unity Netcode ServerRpc doesn't get executed when called by Host itself

Time:01-28

I'm quite new to Unity Netcode. I've been following a few videos around the internet and read the documentation.

I'm trying to make my own lobby system, and to do that I'm trying to keep a NetworkList variable holding a list of a username for each player, just to show the list of players that already joined the server.

My problem is that, when it comes to add to the list host's username itself, I'm not able to call the ServerRpc method that is supposed to update the list.

This is part of the script which is supposed to hold the NetworkList variable and manage to update the list in the UI accordingly.

 public class PlayersListManager : NetworkBehaviour
 {
     [SerializeField] RectTransform playersListTransform;
     [SerializeField] GameObject playerNamePrefab;
 
     public NetworkList<FixedString32Bytes> playerUsernamesList;
 
     private void Awake()
     {
         playerUsernamesList = new NetworkList<FixedString32Bytes>();
         playerUsernamesList.OnListChanged  = OnPlayersListUpdate;
     }
 
     [ServerRpc(RequireOwnership = false)]
     public void AddNewPlayer_ServerRpc(string newUsername)
     {
         Debug.Log("AddNewPlayer_ServerRpc");
         playerUsernamesList.Add(new FixedString32Bytes(newUsername));
     }

     [...]
 }

And this is the Player object which is supposed to send the ServerRpc in order to make the server add its username to the NetworkList.

 public class NetworkPlayer : NetworkBehaviour
 {
     [SerializeField] private string _username; //DEBUG ONLY
     public string Username { get; private set; }
 
     private MainMenuInteraction mainMenuInteraction;
     private PlayersListManager playersListManager;
 
     public override void OnNetworkSpawn()
     {
         base.OnNetworkSpawn();
         mainMenuInteraction = FindObjectOfType<MainMenuInteraction>();
         playersListManager = FindObjectOfType<PlayersListManager>();
         if (IsLocalPlayer)
         {
             Username = mainMenuInteraction.GetUsernameText();
             _username = Username;
             playersListManager.AddNewPlayer_ServerRpc(Username);
         }
     }

     [...]
 }

Basically what happens is that the player is asked to choose its username at first, then he's able to create/join a room, and once he's connected NetworkPlayer will be spawned on the server. Once the NetworkPlayer is spawned, it takes the text from the local InputText, and updates its Username (Here I added a private _username string just to have it serialized on the editor and check what happens). The variale Username is correctly updated with the right username, and then playersListManager.AddNewPlayer_ServerRpc(Username); Is supposed to be called from host's client to be executed on the server, but nothing happens! I've added this line Debug.Log("AddNewPlayer_ServerRpc"); inside the ServerRpc method to check if it ever gets executed, but that doesn't happen.

A "funny" thing is that the ServerRpc gets called correctly from any other client which is not the host.

Reading the ServerRpc documentation makes me understand that there isn't anything wrong with that, since Host acts as a client and a server contemporary, so the host should be able to call and execute the ServerRpc itself. What am I doing wrong here?

CodePudding user response:

After a full day spent on doing as much tests as I could, I found out the problem and fixed it, so I'm writing here the answer in order to let everyone who encounters a similar problem know how to fix this.

I've managed to understand what was happening by adding a button who called on each click a ServerRpc used to increment a simple int. Since that button was successfully incrementing the int, I understood that the problem was that probably OnNetworkSpawn() is too soon to call a ServerRpc, since that OnNetworkSpawn is inside the player NetworkObject, which is spawned by NetworkManager when the host is started.

In order to be sure that the Server is ready to run any ServerRpc, I implemented a coroutine to check if a ServerRpc is actually run.

My NetworkPlayer became then like this:

public override void OnNetworkSpawn()
{
    base.OnNetworkSpawn();
    mainMenuInteraction = FindObjectOfType<MainMenuInteraction>();
    playersListManager = FindObjectOfType<PlayersListManager>();
    if (IsLocalPlayer)
    {
        Username = mainMenuInteraction.GetUsernameText();
        _username = Username;
        StartCoroutine(SendUsernameToServer());
    }
}

private IEnumerator SendUsernameToServer()
{
    if (IsHost)
    {
        do
        {
            mainMenuInteraction.CheckIfServerReady_ServerRpc();
            yield return null;
        } while (!mainMenuInteraction.ServerReady);
    }

    playersListManager.AddNewPlayer_ServerRpc(Username);
    yield return null;
}

where mainMenuInteraction.CheckIfServerReady_ServerRpc(); is as simple as

[ServerRpc]
public void CheckIfServerReady_ServerRpc()
{
    ServerReady = true;
}

with ServerReady being a simple bool initialized to false:

public bool ServerReady { get; private set; } = false;

This way the host's (and just the host's) NetworkPlayer script, on spawn, will wait for this ServerReady to be set to true before calling the actual AddNewPlayer_ServerRpc.

  • Related