Search Unity

Resolved [Rpc(SendTo.Server)] is not executing on the Host

Discussion in 'Netcode for GameObjects' started by KurisC, Jan 25, 2024.

  1. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Here is the function in question specifically:
    Code (CSharp):
    1. [Rpc(SendTo.Server)]
    2.     public void GetPlayerCountServerRpc()
    3.     {
    4.         Debug.Log("Executing GetPlayerCountServerRpc on " + NetworkManager.Singleton.IsServer);
    5.         int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    6.         UpdatePlayerCountClientRpc(playerCount);
    7.     }
    And when I am calling it:
    Code (CSharp):
    1. private void DisplayConnectedPlayerCount()
    2.     {
    3.         // DEACTIVATE GAME OBJECTS TO DISPLAY PLAYERS //
    4.         _hostInputField.gameObject.SetActive(false);
    5.         _portInputField.gameObject.SetActive(false);
    6.         _hostButton.gameObject.SetActive(false);
    7.         _clientButton.gameObject.SetActive(false);
    8.  
    9.         // SET NEXT LOBBY ITEMS ACTIVE
    10.         _connectedPlayersText.gameObject.SetActive(true);
    11.  
    12.         // SHOW START GAME BUTTON - HOST ONLY //
    13.         _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    14.  
    15.         // GET THE PLAYER COUNT FROM THE SERVER
    16.         GetPlayerCountServerRpc();
    17.     }
    If you wish to see the full script:
    Code (CSharp):
    1.  
    2. using TMPro;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using Unity.Netcode;
    6. using Unity.Netcode.Transports.UTP;
    7.  
    8. public class LobbyManager : MonoBehaviour
    9. {
    10.     // INSTANCE //
    11.     public static LobbyManager Instance;
    12.  
    13.     // INPUT FIELDS //
    14.     [SerializeField] private TMP_InputField _hostInputField;
    15.     [SerializeField] private TMP_InputField _portInputField;
    16.  
    17.     // BUTTONS //
    18.     [SerializeField] private Button _hostButton;
    19.     [SerializeField] private Button _clientButton;
    20.     [SerializeField] private Button _startGameButton;
    21.  
    22.     // TEXT //
    23.     [SerializeField] private TextMeshProUGUI _connectedPlayersText;
    24.  
    25.     private void Start()
    26.     {
    27.         // INIT //
    28.         Instance = this;
    29.  
    30.         // ADD LISTENERS //
    31.         _hostButton.onClick.AddListener(StartHost);
    32.         _clientButton.onClick.AddListener(StartClient);
    33.         _startGameButton.onClick.AddListener(StartGame);
    34.  
    35.         // HIDE MAIN LOBBY //
    36.         _startGameButton.gameObject.SetActive(false);
    37.         _connectedPlayersText.gameObject.SetActive(false);
    38.     }
    39.  
    40.     private void StartHost()
    41.     {
    42.         // SET HOST & PORT AND START HOSTING //
    43.         SetConnectionData();
    44.         NetworkManager.Singleton.StartHost();
    45.  
    46.         // NEXT STAGE OF LOBBY //
    47.         DisplayConnectedPlayerCount();
    48.     }
    49.  
    50.     private void StartClient()
    51.     {
    52.         // SET HOST & PORT AND JOIN HOST SESSION //
    53.         SetConnectionData();
    54.         NetworkManager.Singleton.StartClient();
    55.  
    56.         // NEXT STAGE OF LOBBY //
    57.         DisplayConnectedPlayerCount();
    58.     }
    59.  
    60.     private void StartGame()
    61.     {
    62.         if (_startGameButton.gameObject.GetComponent<Image>().color == Color.gray)
    63.         {
    64.             return;
    65.         }
    66.  
    67.         else
    68.         {
    69.             NetworkManager.Singleton.SceneManager.LoadScene("GameScene", UnityEngine.SceneManagement.LoadSceneMode.Single);
    70.         }
    71.     }
    72.  
    73.     [Rpc(SendTo.Server)]
    74.     public void GetPlayerCountServerRpc()
    75.     {
    76.         Debug.Log("Executing GetPlayerCountServerRpc on " + NetworkManager.Singleton.IsServer);
    77.         int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    78.         UpdatePlayerCountClientRpc(playerCount);
    79.     }
    80.  
    81.     [ClientRpc]
    82.     public void UpdatePlayerCountClientRpc(int playerCount)
    83.     {
    84.         _connectedPlayersText.text = $"Connected Players: {playerCount}";
    85.         if (_startGameButton.gameObject.activeSelf)
    86.         {
    87.             if (playerCount != 2)
    88.             {
    89.                 _startGameButton.gameObject.GetComponent<Image>().color = Color.gray;
    90.             }
    91.             else
    92.             {
    93.                 _startGameButton.gameObject.GetComponent<Image>().color = new(0.3287202f, 0.8396226f, 0.4267554f);
    94.             }
    95.         }
    96.     }
    97.  
    98.     private void DisplayConnectedPlayerCount()
    99.     {
    100.         // DEACTIVATE GAME OBJECTS TO DISPLAY PLAYERS //
    101.         _hostInputField.gameObject.SetActive(false);
    102.         _portInputField.gameObject.SetActive(false);
    103.         _hostButton.gameObject.SetActive(false);
    104.         _clientButton.gameObject.SetActive(false);
    105.  
    106.         // SET NEXT LOBBY ITEMS ACTIVE
    107.         _connectedPlayersText.gameObject.SetActive(true);
    108.  
    109.         // SHOW START GAME BUTTON - HOST ONLY //
    110.         _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    111.  
    112.         // GET THE PLAYER COUNT FROM THE SERVER
    113.         GetPlayerCountServerRpc();
    114.     }
    115.  
    116.     private void SetConnectionData()
    117.     {
    118.         var _unityTransport = NetworkManager.Singleton.GetComponent<UnityTransport>();
    119.  
    120.         if (string.IsNullOrEmpty(_hostInputField.text) && string.IsNullOrEmpty(_portInputField.text))
    121.         {
    122.             _unityTransport.SetConnectionData("127.0.0.1", 7777);
    123.         }
    124.  
    125.         else
    126.         {
    127.             _unityTransport.SetConnectionData(_hostInputField.text, ushort.Parse(_portInputField.text));
    128.         }
    129.     }
    130. }

    I am using the Latest Version of Netcode, the script is attached to the canvas in my Lobby screen, I have my NetworkManager set up as follows:
     
  2. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Should have clarified, but I am aware that it is not running on the server due to the Debug message returning False, inside the ServerRPC function.
     
  3. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Change your RPC method to look like this:

    Code (CSharp):
    1.  
    2.     [Rpc(SendTo.Server)]
    3.     public void GetPlayerCountRpc()
    4.     {
    5.         Debug.Log("Executing GetPlayerCountRpc on " + NetworkManager.Singleton.IsServer);
    6.         int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    7.         UpdatePlayerCountClientRpc(playerCount);
    8.     }
    Since we still honor the legacy ServerRpc and ClientRpc attributes, most likely the method is not being recognized as the a universal RPC method.

    All universal RPC methods should end with just "Rpc" and you should avoid using "ServerRpc" and "ClientRpc".
    If you want to identify it specific to the SendTo target, then you could do something like:
    ServerGetPlayerCountRpc.

    Let me know if this resolves your issue?
     
  4. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Hello, thanks for the quick reply! I changed the function name as you suggested. The debug message still reads as follows:


    I am referring to this debug message inside the ServerRpc: Debug.Log("Executing GetPlayerCountRpc on " + NetworkManager.Singleton.IsServer);

    This then leads the client side game to crash because NetworkManager.Singleton.ConnectedClientsList.Count can only be accessed on the server.
     
    Last edited: Jan 25, 2024
  5. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    That would mean it is not actually sending the RPC itself but rather it is just directly invoking it on the client side.

    However, if you want to get the connected client count that is not server specific you can always use:
    NetworkManager.ConnectedClientsIds

    That is populated on clients when a new client joins now.

    Here is an adjustment to your method so you can troubleshoot it faster:

    Code (CSharp):
    1.      
    2.         [Rpc(SendTo.Server)]
    3.         public void GetPlayerCountRpc(ulong sender, RpcParams rpcParams = default)
    4.         {
    5.             Debug.Log($"[Received by Client-{rpcParams.Receive.SenderClientId}][Sender={sender}] Executing GetPlayerCountRpc on " + IsServer);
    6.             if (IsServer)
    7.             {
    8.                 int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    9.                 UpdatePlayerCountClientRpc(playerCount);
    10.             }
    11.             else if (sender != rpcParams.Receive.SenderClientId)
    12.             {
    13.                 Debug.LogError($"The sanity check sender ({sender}) != the Receive.SenderClientId({rpcParams.Receive.SenderClientId})!");
    14.             }
    15.         }
    16.         private void DisplayConnectedPlayerCount()
    17.         {
    18.             // DEACTIVATE GAME OBJECTS TO DISPLAY PLAYERS //
    19.             _hostInputField.gameObject.SetActive(false);
    20.             _portInputField.gameObject.SetActive(false);
    21.             _hostButton.gameObject.SetActive(false);
    22.             _clientButton.gameObject.SetActive(false);
    23.             // SET NEXT LOBBY ITEMS ACTIVE
    24.             _connectedPlayersText.gameObject.SetActive(true);
    25.             // SHOW START GAME BUTTON - HOST ONLY //
    26.             _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    27.             // GET THE PLAYER COUNT FROM THE SERVER
    28.             GetPlayerCountRpc(NetworkManager.LocalClientId);
    29.         }
    30.  
    Let me know what the end result of those changes do and if you get that LogError message?
     
  6. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    You can also subscribe to NetworkManager.OnConnectionEvent that will trigger on clients as well.
    This will get invoked (on the server and clients) when a client connects and disconnects from the session. It includes a ConnectionEventData struct that provides the details of the connection event.
     
  7. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Hello, appreciate the response again! I have updated both the Rpc and DisplayConnectedPlayerCount to match exactly as you did, so here is the full script once again:
    Code (CSharp):
    1.  
    2. using TMPro;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using Unity.Netcode;
    6. using Unity.Netcode.Transports.UTP;
    7.  
    8. public class LobbyManager : MonoBehaviour
    9. {
    10.     // INSTANCE //
    11.     public static LobbyManager Instance;
    12.  
    13.     // INPUT FIELDS //
    14.     [SerializeField] private TMP_InputField _hostInputField;
    15.     [SerializeField] private TMP_InputField _portInputField;
    16.  
    17.     // BUTTONS //
    18.     [SerializeField] private Button _hostButton;
    19.     [SerializeField] private Button _clientButton;
    20.     [SerializeField] private Button _startGameButton;
    21.  
    22.     // TEXT //
    23.     [SerializeField] private TextMeshProUGUI _connectedPlayersText;
    24.  
    25.     private void Start()
    26.     {
    27.         // INIT //
    28.         Instance = this;
    29.  
    30.         // ADD LISTENERS //
    31.         _hostButton.onClick.AddListener(StartHost);
    32.         _clientButton.onClick.AddListener(StartClient);
    33.         _startGameButton.onClick.AddListener(StartGame);
    34.  
    35.         // HIDE MAIN LOBBY //
    36.         _startGameButton.gameObject.SetActive(false);
    37.         _connectedPlayersText.gameObject.SetActive(false);
    38.     }
    39.  
    40.     private void StartHost()
    41.     {
    42.         // SET HOST & PORT AND START HOSTING //
    43.         SetConnectionData();
    44.         NetworkManager.Singleton.StartHost();
    45.  
    46.         // NEXT STAGE OF LOBBY //
    47.         DisplayConnectedPlayerCount();
    48.     }
    49.  
    50.     private void StartClient()
    51.     {
    52.         // SET HOST & PORT AND JOIN HOST SESSION //
    53.         SetConnectionData();
    54.         NetworkManager.Singleton.StartClient();
    55.  
    56.         // NEXT STAGE OF LOBBY //
    57.         DisplayConnectedPlayerCount();
    58.     }
    59.  
    60.     private void StartGame()
    61.     {
    62.         if (_startGameButton.gameObject.GetComponent<Image>().color == Color.gray)
    63.         {
    64.             return;
    65.         }
    66.  
    67.         else
    68.         {
    69.             NetworkManager.Singleton.SceneManager.LoadScene("GameScene", UnityEngine.SceneManagement.LoadSceneMode.Single);
    70.         }
    71.     }
    72.  
    73.     [Rpc(SendTo.Server)]
    74.     public void GetPlayerCountRpc(ulong sender, RpcParams rpcParams = default)
    75.     {
    76.         Debug.Log($"[Received by Client-{rpcParams.Receive.SenderClientId}][Sender={sender}] Executing GetPlayerCountRpc on " + NetworkManager.Singleton.IsServer);
    77.         if (NetworkManager.Singleton.IsServer)
    78.         {
    79.             int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    80.             UpdatePlayerCountClientRpc(playerCount);
    81.         }
    82.         else if (sender != rpcParams.Receive.SenderClientId)
    83.         {
    84.             Debug.LogError($"The sanity check sender ({sender}) != the Receive.SenderClientId({rpcParams.Receive.SenderClientId})!");
    85.         }
    86.     }
    87.  
    88.     [ClientRpc]
    89.     public void UpdatePlayerCountClientRpc(int playerCount)
    90.     {
    91.         _connectedPlayersText.text = $"Connected Players: {playerCount}";
    92.         if (_startGameButton.gameObject.activeSelf)
    93.         {
    94.             if (playerCount != 2)
    95.             {
    96.                 _startGameButton.gameObject.GetComponent<Image>().color = Color.gray;
    97.             }
    98.             else
    99.             {
    100.                 _startGameButton.gameObject.GetComponent<Image>().color = new(0.3287202f, 0.8396226f, 0.4267554f);
    101.             }
    102.         }
    103.     }
    104.  
    105.     private void DisplayConnectedPlayerCount()
    106.     {
    107.         // DEACTIVATE GAME OBJECTS TO DISPLAY PLAYERS //
    108.         _hostInputField.gameObject.SetActive(false);
    109.         _portInputField.gameObject.SetActive(false);
    110.         _hostButton.gameObject.SetActive(false);
    111.         _clientButton.gameObject.SetActive(false);
    112.  
    113.         // SET NEXT LOBBY ITEMS ACTIVE
    114.         _connectedPlayersText.gameObject.SetActive(true);
    115.  
    116.         // SHOW START GAME BUTTON - HOST ONLY //
    117.         _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    118.  
    119.         // GET THE PLAYER COUNT FROM THE SERVER
    120.         GetPlayerCountRpc(NetworkManager.Singleton.LocalClientId);
    121.     }
    122.  
    123.     private void SetConnectionData()
    124.     {
    125.         var _unityTransport = NetworkManager.Singleton.GetComponent<UnityTransport>();
    126.  
    127.         if (string.IsNullOrEmpty(_hostInputField.text) && string.IsNullOrEmpty(_portInputField.text))
    128.         {
    129.             _unityTransport.SetConnectionData("127.0.0.1", 7777);
    130.         }
    131.  
    132.         else
    133.         {
    134.             _unityTransport.SetConnectionData(_hostInputField.text, ushort.Parse(_portInputField.text));
    135.         }
    136.     }
    137. }

    This time, the Debug returned reads as follows when running on the Client:
    [Received by Client-0][Sender=0] Executing GetPlayerCountRpc on False

    Here is all the Components of the Object in which this script is attached to in the scene (it is the canvas)
     
  8. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Let me put together a quick test to verify this.
     
  9. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Before you waste your time (hopefully you are still here) changing the script to from Monobehaviour to NetworkBehaviour has changed the error I am receiving:
    KeyNotFoundException: The given key 'LobbyManager' was not present in the dictionary.

    I also added a NetworkObject to the canvas that the script is attached to as part of this. Let me know if this is heading in the right direction or I am going off track.

    Full Error Message
    KeyNotFoundException: The given key 'LobbyManager' was not present in the dictionary. System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <834b2ded5dad441e8c7a4287897d63c7>:0) Unity.Netcode.BaseRpcTarget.SendMessageToClient (Unity.Netcode.NetworkBehaviour behaviour, System.UInt64 clientId, Unity.Netcode.RpcMessage& message, Unity.Netcode.NetworkDelivery delivery) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.8.0/Runtime/Messaging/RpcTargets/BaseRpcTarget.cs:45) Unity.Netcode.DirectSendRpcTarget.Send (Unity.Netcode.NetworkBehaviour behaviour, Unity.Netcode.RpcMessage& message, Unity.Netcode.NetworkDelivery delivery, Unity.Netcode.RpcParams rpcParams) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.8.0/Runtime/Messaging/RpcTargets/DirectSendRpcTarget.cs:16) Unity.Netcode.ServerRpcTarget.Send (Unity.Netcode.NetworkBehaviour behaviour, Unity.Netcode.RpcMessage& message, Unity.Netcode.NetworkDelivery delivery, Unity.Netcode.RpcParams rpcParams) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.8.0/Runtime/Messaging/RpcTargets/ServerRpcTarget.cs:29) Unity.Netcode.NetworkBehaviour.__endSendRpc (Unity.Netcode.FastBufferWriter& bufferWriter, System.UInt32 rpcMethodId, Unity.Netcode.RpcParams rpcParams, Unity.Netcode.RpcAttribute+RpcAttributeParams attributeParams, Unity.Netcode.SendTo defaultTarget, Unity.Netcode.RpcDelivery rpcDelivery) (at ./Library/PackageCache/com.unity.netcode.gameobjects@1.8.0/Runtime/Core/NetworkBehaviour.cs:389) LobbyManager.GetPlayerCountRpc (System.UInt64 sender, Unity.Netcode.RpcParams rpcParams) (at Assets/Scripts/Lobby/LobbyManager.cs:75) LobbyManager.DisplayConnectedPlayerCount () (at Assets/Scripts/Lobby/LobbyManager.cs:119) LobbyManager.StartClient () (at Assets/Scripts/Lobby/LobbyManager.cs:56) UnityEngine.Events.InvokableCall.Invoke () (at <b1fe495152fd4f0180f79e56e3bccacc>:0) UnityEngine.Events.UnityEvent.Invoke () (at <b1fe495152fd4f0180f79e56e3bccacc>:0) UnityEngine.UI.Button.Press () (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70) UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114) UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:57) UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272) UnityEngine.EventSystems.EventSystem:Update() (at ./Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:530)
     
  10. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Oh definitely that would impact things... and it would not even recognize the Rpc methods if in a Monobehaviour.
    A rule of thumb for NetworkBehaviour components and what they need to be attached to is:
    A NetworkBehaviour has to be attached to a GameObject with a NetworkObject component or on a child GameObject who has a parent or distant parent that has a NetworkObject component (i.e. you can place NetworkBehaviour components at any child scope as long as the root or a parent above the child GameObject with NetworkBehaviour component has a NetworkObjectComponent on it). The best way to always make sure is that the root parent has the NetworkObject component.
     
  11. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Coming back to this to say that I think this particular error is coming down to me attempting to use a NetworkBehaviour script prior to setting up the Server/Host/Client which is what this script is supposed to do (start the Host or Client).
     
  12. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    That makes a lot of sense, is the solution here then to separate the code into two scripts, have the LobbyManager.cs script be a Monobehaviour that sets up the server, and then a separate NetworkBehaviourScript that runs the RPCs, and if this is the case, can these be attached to the same gameobject.
     
  13. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    I have attempted to do this, I have split the script into two scripts, isolating the Network behaviour and the Monobehaviour functions.

    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using Unity.Netcode;
    5. using Unity.Netcode.Transports.UTP;
    6.  
    7. public class SetupGame : MonoBehaviour
    8. {
    9.     // INPUT FIELDS //
    10.     [SerializeField] private TMP_InputField _hostInputField;
    11.     [SerializeField] private TMP_InputField _portInputField;
    12.  
    13.     // BUTTONS //
    14.     [SerializeField] private Button _hostButton;
    15.     [SerializeField] private Button _clientButton;
    16.  
    17.     private void Start()
    18.     {
    19.  
    20.         // ADD LISTENERS //
    21.         _hostButton.onClick.AddListener(StartHost);
    22.         _clientButton.onClick.AddListener(StartClient);
    23.     }
    24.  
    25.     private void StartHost()
    26.     {
    27.         // SET HOST & PORT AND START HOSTING //
    28.         SetConnectionData();
    29.         NetworkManager.Singleton.StartHost();
    30.  
    31.         // NEXT STAGE OF LOBBY //
    32.         EnterWaitingLobby();
    33.     }
    34.  
    35.     private void StartClient()
    36.     {
    37.         // SET HOST & PORT AND JOIN HOST SESSION //
    38.         SetConnectionData();
    39.         NetworkManager.Singleton.StartClient();
    40.  
    41.         // NEXT STAGE OF LOBBY //
    42.         EnterWaitingLobby();
    43.     }
    44.  
    45.     private void EnterWaitingLobby()
    46.     {
    47.         // DEACTIVATE GAME OBJECTS TO DISPLAY PLAYERS //
    48.         _hostInputField.gameObject.SetActive(false);
    49.         _portInputField.gameObject.SetActive(false);
    50.         _hostButton.gameObject.SetActive(false);
    51.         _clientButton.gameObject.SetActive(false);
    52.  
    53.         // ENTER LOBBY //
    54.         WaitingLobby.Instance.Enter();
    55.     }
    56.  
    57.     private void SetConnectionData()
    58.     {
    59.         var _unityTransport = NetworkManager.Singleton.GetComponent<UnityTransport>();
    60.  
    61.         if (string.IsNullOrEmpty(_hostInputField.text) && string.IsNullOrEmpty(_portInputField.text))
    62.         {
    63.             _unityTransport.SetConnectionData("127.0.0.1", 7777);
    64.         }
    65.  
    66.         else
    67.         {
    68.             _unityTransport.SetConnectionData(_hostInputField.text, ushort.Parse(_portInputField.text));
    69.         }
    70.     }
    71. }
    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using Unity.Netcode;
    5. using Unity.Netcode.Transports.UTP;
    6.  
    7. public class WaitingLobby : NetworkBehaviour
    8. {
    9.     // INSTANCE //
    10.     public static WaitingLobby Instance;
    11.  
    12.     // UI ELEMENTS //
    13.     [SerializeField] private Button _startGameButton;
    14.     [SerializeField] private TextMeshProUGUI _connectedPlayersText;
    15.  
    16.     private void Start()
    17.     {
    18.         Instance = this;
    19.         _startGameButton.gameObject.SetActive(false);
    20.         _connectedPlayersText.gameObject.SetActive(false);
    21.     }
    22.  
    23.     public void Enter()
    24.     {
    25.         // SHOW START GAME BUTTON - HOST ONLY //
    26.         _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    27.  
    28.         // SHOW CONNECTED PLAYERS TEXT //
    29.         _connectedPlayersText.gameObject.SetActive(true);
    30.         GetPlayerCountRpc();
    31.     }
    32.  
    33.     [Rpc(SendTo.Server)]
    34.     public void GetPlayerCountRpc(RpcParams rpcParams = default)
    35.     {
    36.         Debug.Log("Executing GetPlayerCountRpc on " + NetworkManager.Singleton.IsServer);
    37.         if (NetworkManager.Singleton.IsServer)
    38.         {
    39.             int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    40.             UpdatePlayerCountRpc(playerCount);
    41.         }
    42.     }
    43.  
    44.     [Rpc(SendTo.Me)]
    45.     public void UpdatePlayerCountRpc(int playerCount)
    46.     {
    47.         _connectedPlayersText.text = $"Connected Players: {playerCount}";
    48.         if (_startGameButton.gameObject.activeSelf)
    49.         {
    50.             if (playerCount != 2)
    51.             {
    52.                 _startGameButton.gameObject.GetComponent<Image>().color = Color.gray;
    53.             }
    54.  
    55.             else
    56.             {
    57.                 _startGameButton.gameObject.GetComponent<Image>().color = new(0.3287202f, 0.8396226f, 0.4267554f);
    58.             }
    59.         }
    60.     }
    61. }
    They are both attached to the same Canvas, which has the Network Object on it. Again, as soon as I enter the ServerRpc, the KeyNotFoundException appears.
     
  14. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    o_O
    Well... yeah you need to start either a Host/Server and then join client instances to be able to send messages between the instances.

    Here is a project that demonstrates using Universal RPCs and has some additional stuff... it can be built for a dedicated server and such.

    You might find it a useful template to prototype ideas/concepts without having to reconfigure everything.
    ;)
     

    Attached Files:

  15. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Thanks for sending this demo, I will give it a look to see what I can change up. To clarify, I am setting up a Server first and then joining as a Client before the RPC is ever called.
     
  16. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Regarding isolating scripts... it really depends on context and what you are planning to do.
    If you "separate your netcode-peas from your monobehaviour-carrots", then you will want to really think about how information will be exchanged. You can get into trouble making things singletons or making static properties just to share specific information.

    On the other hand, you can extend specific events from NetworkBehaviours to MonoBehaviours using UnityEvents within your NetworkBehaviour derived classes.
    As an example, if you have a NetworkBehaviour that has some NetworkVariable you want to easily connect to MonoBehaviours when the value changes then you can add a UnityEvent<T> that passes the same type used to declare your NetworkVariable<T> (type T) and then create public methods (i.e. public void SomethingChanged(T something) within your MonoBheaviours that you can just assign to the UnityEvent<T>.

    But it really depends upon what you are trying to accomplish.
     
  17. KurisC

    KurisC

    Joined:
    Feb 9, 2021
    Posts:
    14
    Unfortunately not been the best at explaining myself, so I will attempt to do so with images.

    1. This script runs in the Server Set Up + Lobby Screen.

    2. When joining as the Host, this is how the Lobby screen looks, and it is after creating the server (as a Host) that the first Server RPC is called (to itself, as it is the server in this case, from my understanding), up until this point nothing as such as been called. The image, and debugging from the server, shows that it runs fine from the server, naturally.

    This is the ServerRpc being called at this point:
    Code (CSharp):
    1. [Rpc(SendTo.Server)]
    2.     public void GetPlayerCountRpc(RpcParams rpcParams = default)
    3.     {
    4.         Debug.Log("Executing GetPlayerCountRpc on " + NetworkManager.Singleton.IsServer);
    5.         if (NetworkManager.Singleton.IsServer)
    6.         {
    7.             int playerCount = NetworkManager.Singleton.ConnectedClientsList.Count;
    8.             UpdatePlayerCountRpc(playerCount);
    9.         }
    10.     }
    11.  

    3. I then press Join game on the next Unity instance, to join the server as a client (same IP, same Port), which should then again run the ServerRPC to display the connected players, however this breaks it, since ensuring I have set up the script as NetworkBehaviour, this is breaking due to a KeyNotFoundException error, with the given key being the name of my script that contains the RPC call.

    Further debugging on the Client version shows that I join the Client fine:
    Code (CSharp):
    1. private void StartClient()
    2.     {
    3.         // SET HOST & PORT AND JOIN HOST SESSION //
    4.         SetConnectionData();
    5.         NetworkManager.Singleton.StartClient();
    6.         Debug.Log("Working");
    7.  
    8.         // NEXT STAGE OF LOBBY //
    9.         EnterWaitingLobby();
    10.     }
    The Debug.Log message appears here.

    Code (CSharp):
    1. public void Enter()
    2.     {
    3.         // SHOW START GAME BUTTON - HOST ONLY //
    4.         _startGameButton.gameObject.SetActive(NetworkManager.Singleton.IsHost);
    5.  
    6.         // SHOW CONNECTED PLAYERS TEXT //
    7.         _connectedPlayersText.gameObject.SetActive(true);
    8.         Debug.Log("Working");
    9.         GetPlayerCountRpc();
    10.     }
    It also appears here, just one line prior to entering the Rpc. Checking the Network object shows that I am connected as a Client.
     
    Last edited: Jan 25, 2024
  18. splundge

    splundge

    Joined:
    Dec 29, 2021
    Posts:
    24
    I am also experiencing the same issue. key not found. Trying to write a simple chat client, following this simple 5 minute tutorial.


    Although, reading through this thread, i don't know how you guys are using
    Code (CSharp):
    1. [Rpc(SendTo.Server)]
    I get errors saying that `Rpc` is an abstract class and cannot be used....
     
    Last edited: Mar 7, 2024
  19. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    This might not solve your issue with the RPC usage, but you can use the NetworkManager.ConnectedClientsIds to get the current connected user count. This is updated on both server/host and clients when they connect and disconnect.
     
  20. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    Is that a typo or is your code missing the close parenthesis?
    Code (CSharp):
    1. [Rpc(SendTo.Server)]
     
  21. splundge

    splundge

    Joined:
    Dec 29, 2021
    Posts:
    24
    Sorry, yes, that was a typo. I just corrected it.

    I can only use
    Code (CSharp):
    1.  
    2. [ServerRpcAttribute]
    3. [ClientRpcAttribute]
    4.  
    Maybe we're using different versions. I am using netcode for game objects 1.7.1

    I've been stuck on this 'key not found' issue for a week. My next steps are to completely isolate the code down in a new project. If i can't get it working, ill make a new thread
     
  22. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    258
    You need to update to NGO v1.8.x to have the universal RPC feature set (recommend v1.8.1 due to the hot fix).